hwmTransporter

Перемещения по карте, поиск работы и засады с любой страницы

目前為 2023-10-28 提交的版本,檢視 最新版本

// ==UserScript==
// @name		   hwmTransporter
// @namespace	   Tamozhnya1
// @version		   8.9
// @description	   Перемещения по карте, поиск работы и засады с любой страницы
// @author		   Tamozhnya1
// @encoding	   utf-8
// @include		   *heroeswm.ru/*
// @include		   *lordswm.com/*
// @grant		   GM_log
// @grant		   GM_listValues
// @grant		   GM_setValue
// @grant		   GM_getValue
// @grant		   GM_deleteValue
// @grant 		   GM.xmlHttpRequest
// @grant 		   GM.notification
// @license        MIT
// ==/UserScript==

if(typeof GM_getValue != 'function') {
    this.GM_getValue = function (key,def) { return localStorage[key] || def; };
    this.GM_setValue = function (key,value) { return localStorage[key] = value; };
    this.GM_deleteValue = function (key) { return delete localStorage[key]; };
}
const playerIdMatch = document.cookie.match(/pl_id=(\d+)/);
if(playerIdMatch) {
    var PlayerId = playerIdMatch[1];
}
if(!PlayerId) {
    GM_deleteValue("TransporterUserName");
    return;
}
const isEn = document.documentElement.lang == "en";
var AmbushState = { Setted: 1, Battle: 2 };
var finalResultDiv;
const isHeartOnPage = document.querySelector("canvas#heart") || document.querySelector("div#heart_js_mobile");

// Граф всех возможных путей перемещения по карте
const routes = [null,
[null,null,["1-2",1],["1-3",1],["1-4",1.4],["1-5",1],["1-3-6",2],["1-7",1.4],["1-8",1],["1-3-9",2.4],["1-5-10",2],["1-11",1.4],["1-12",1.4],["1-8-13",2.4],["1-2-14",2],["1-2-15",2.4],["1-4-16",2.8],["1-2-14-17",3],["1-2-14-18",3.4],["1-5-19",2.4],["1-5-10-20",3.4],["1-5-19-21",3.8],["1-5-10-20-22",4.8],["1-12-23",2.8],["1-3-24",2.4],null,["1-5-26",2.4],["1-8-27",2]],
[null,["2-1",1],null,["2-3",1.4],["2-4",1],["2-5",1.4],["2-3-6",2.4],["2-1-7",2.4],["2-1-8",2],["2-3-9",2.8],["2-5-10",2.4],["2-11",1],["2-1-12",2.4],["2-1-8-13",3.4],["2-14",1],["2-15",1.4],["2-4-16",2.4],["2-14-17",2],["2-14-18",2.4],["2-11-19",2],["2-11-19-20",3],["2-11-19-21",3.4],["2-11-19-20-22",4.4],["2-3-9-23",3.8],["2-4-24",2],null,["2-5-26",2.8],["2-1-8-27",3]],
[null,["3-1",1],["3-2",1.4],null,["3-4",1],["3-1-5",2],["3-6",1],["3-1-7",2.4],["3-8",1.4],["3-9",1.4],["3-1-5-10",3],["3-1-11",2.4],["3-12",1],["3-12-13",2],["3-2-14",2.4],["3-4-15",2],["3-4-16",2.4],["3-2-14-17",3.4],["3-4-15-18",3],["3-1-5-19",3.4],["3-1-5-10-20",4.4],["3-1-5-19-21",4.8],["3-1-5-19-21-22",5.8],["3-9-23",2.4],["3-24",1.4],null,["3-1-5-26",3.4],["3-8-27",2.4]],
[null,["4-1",1.4],["4-2",1],["4-3",1],null,["4-1-5",2.4],["4-6",1.4],["4-1-7",2.8],["4-1-8",2.4],["4-3-9",2.4],["4-1-5-10",3.4],["4-2-11",2],["4-3-12",2],["4-3-12-13",3],["4-14",1.4],["4-15",1],["4-16",1.4],["4-14-17",2.4],["4-15-18",2],["4-2-11-19",3],["4-2-11-19-20",4],["4-2-11-19-21",4.4],["4-2-11-19-20-22",5.4],["4-3-9-23",3.4],["4-24",1],null,["4-1-5-26",3.8],["4-1-8-27",3.4]],
[null,["5-1",1],["5-2",1.4],["5-1-3",2],["5-1-4",2.4],null,["5-1-3-6",3],["5-7",1],["5-8",1.4],["5-1-3-9",3.4],["5-10",1],["5-11",1],["5-1-12",2.4],["5-8-13",2.8],["5-2-14",2.4],["5-2-15",2.8],["5-1-4-16",3.8],["5-2-14-17",3.4],["5-2-14-18",3.8],["5-19",1.4],["5-10-20",2.4],["5-19-21",2.8],["5-10-20-22",3.8],["5-1-12-23",3.8],["5-1-3-24",3.4],null,["5-26",1.4],["5-7-27",2.4]],
[null,["6-3-1",2],["6-3-2",2.4],["6-3",1],["6-4",1.4],["6-3-1-5",3],null,["6-3-1-7",3.4],["6-3-8",2.4],["6-9",1],["6-3-1-5-10",4],["6-3-1-11",3.4],["6-12",1.4],["6-9-13",2.4],["6-4-14",2.8],["6-4-15",2.4],["6-4-16",2.8],["6-4-14-17",3.8],["6-4-15-18",3.4],["6-3-1-5-19",4.4],["6-3-1-5-10-20",5.4],["6-3-1-5-19-21",5.8],["6-3-1-5-10-20-22",6.8],["6-9-23",2],["6-24",1],null,["6-3-1-5-26",4.4],["6-12-27",2.8]],
[null,["7-1",1.4],["7-1-2",2.4],["7-1-3",2.4],["7-1-4",2.8],["7-5",1],["7-1-3-6",3.4],null,["7-8",1],["7-8-12-9",3],["7-10",1.4],["7-5-11",2],["7-8-12",2],["7-8-13",2.4],["7-1-2-14",3.4],["7-1-2-15",3.8],["7-1-4-16",4.2],["7-1-2-14-17",4.4],["7-1-2-14-18",4.8],["7-5-19",2.4],["7-10-20",2.8],["7-5-19-21",3.8],["7-10-20-22",4.2],["7-8-12-23",3.4],["7-1-3-24",3.8],null,["7-26",1],["7-27",1.4]],
[null,["8-1",1],["8-1-2",2],["8-3",1.4],["8-1-4",2.4],["8-5",1.4],["8-3-6",2.4],["8-7",1],null,["8-12-9",2],["8-5-10",2.4],["8-1-11",2.4],["8-12",1],["8-13",1.4],["8-1-2-14",3],["8-1-2-15",3.4],["8-1-4-16",3.8],["8-1-2-14-17",4],["8-1-2-14-18",4.4],["8-5-19",2.8],["8-5-10-20",3.8],["8-5-19-21",4.2],["8-5-10-20-22",5.2],["8-12-23",2.4],["8-3-24",2.8],null,["8-7-26",2],["8-27",1]],
[null,["9-3-1",2.4],["9-3-2",2.8],["9-3",1.4],["9-3-4",2.4],["9-3-1-5",3.4],["9-6",1],["9-12-8-7",3],["9-12-8",2],null,["9-3-1-5-10",4.4],["9-3-1-11",3.8],["9-12",1],["9-13",1.4],["9-3-2-14",3.8],["9-3-4-15",3.4],["9-3-4-16",3.8],["9-3-2-14-17",4.8],["9-3-4-15-18",4.4],["9-3-1-5-19",4.8],["9-3-1-5-10-20",5.8],["9-3-1-5-19-21",6.2],["9-3-1-5-10-20-22",7.2],["9-23",1],["9-6-24",2],null,["9-12-8-7-26",4],["9-12-27",2.4]],
[null,["10-5-1",2],["10-5-2",2.4],["10-5-1-3",3],["10-5-1-4",3.4],["10-5",1],["10-5-1-3-6",4],["10-7",1.4],["10-5-8",2.4],["10-5-1-3-9",4.4],null,["10-11",1.4],["10-5-1-12",3.4],["10-5-8-13",3.8],["10-11-14",2.8],["10-5-2-15",3.8],["10-5-1-4-16",4.8],["10-11-14-17",3.8],["10-11-14-18",4.2],["10-19",1],["10-20",1.4],["10-19-21",2.4],["10-20-22",2.8],["10-5-1-12-23",4.8],["10-5-1-3-24",4.4],null,["10-26",1],["10-7-27",2.8]],
[null,["11-1",1.4],["11-2",1],["11-1-3",2.4],["11-2-4",2],["11-5",1],["11-1-3-6",3.4],["11-5-7",2],["11-1-8",2.4],["11-1-3-9",3.8],["11-10",1.4],null,["11-1-12",2.8],["11-1-8-13",3.8],["11-14",1.4],["11-2-15",2.4],["11-2-4-16",3.4],["11-14-17",2.4],["11-14-18",2.8],["11-19",1],["11-19-20",2],["11-19-21",2.4],["11-19-20-22",3.4],["11-1-12-23",4.2],["11-2-4-24",3],null,["11-5-26",2.4],["11-5-7-27",3.4]],
[null,["12-1",1.4],["12-1-2",2.4],["12-3",1],["12-3-4",2],["12-1-5",2.4],["12-6",1.4],["12-8-7",2],["12-8",1],["12-9",1],["12-1-5-10",3.4],["12-1-11",2.8],null,["12-13",1],["12-1-2-14",3.4],["12-3-4-15",3],["12-3-4-16",3.4],["12-1-2-14-17",4.4],["12-3-4-15-18",4],["12-1-5-19",3.8],["12-1-5-10-20",4.8],["12-1-5-19-21",5.2],["12-1-5-10-20-22",6.2],["12-23",1.4],["12-3-24",2.4],null,["12-8-7-26",3],["12-27",1.4]],
[null,["13-8-1",2.4],["13-8-1-2",3.4],["13-12-3",2],["13-12-3-4",3],["13-8-5",2.8],["13-9-6",2.4],["13-8-7",2.4],["13-8",1.4],["13-9",1.4],["13-8-5-10",3.8],["13-8-1-11",3.8],["13-12",1],null,["13-8-1-2-14",4.4],["13-12-3-4-15",4],["13-12-3-4-16",4.4],["13-8-1-2-14-17",5.4],["13-12-3-4-15-18",5],["13-8-5-19",4.2],["13-8-5-10-20",5.2],["13-8-5-19-21",5.6],["13-8-5-10-20-22",6.6],["13-23",1],["13-9-6-24",3.4],null,["13-8-7-26",3.4],["13-27",1]],
[null,["14-2-1",2],["14-2",1],["14-2-3",2.4],["14-4",1.4],["14-2-5",2.4],["14-4-6",2.8],["14-2-1-7",3.4],["14-2-1-8",3],["14-2-3-9",3.8],["14-11-10",2.8],["14-11",1.4],["14-2-1-12",3.4],["14-2-1-8-13",4.4],null,["14-15",1],["14-15-16",2],["14-17",1],["14-18",1.4],["14-11-19",2.4],["14-11-19-20",3.4],["14-11-19-21",3.8],["14-11-19-20-22",4.8],["14-2-3-9-23",4.8],["14-4-24",2.4],null,["14-2-5-26",3.8],["14-2-1-8-27",4]],
[null,["15-2-1",2.4],["15-2",1.4],["15-4-3",2],["15-4",1],["15-2-5",2.8],["15-4-6",2.4],["15-2-1-7",3.8],["15-2-1-8",3.4],["15-4-3-9",3.4],["15-2-5-10",3.8],["15-2-11",2.4],["15-4-3-12",3],["15-4-3-12-13",4],["15-14",1],null,["15-16",1],["15-17",1.4],["15-18",1],["15-2-11-19",3.4],["15-2-11-19-20",4.4],["15-2-11-19-21",4.8],["15-2-11-19-21-22",5.8],["15-4-3-9-23",4.4],["15-24",1.4],null,["15-2-5-26",4.2],["15-2-1-8-27",4.4]],
[null,["16-4-1",2.8],["16-4-2",2.4],["16-4-3",2.4],["16-4",1.4],["16-4-1-5",3.8],["16-4-6",2.8],["16-4-1-7",4.2],["16-4-1-8",3.8],["16-4-3-9",3.8],["16-4-1-5-10",4.8],["16-4-2-11",3.4],["16-4-3-12",3.4],["16-4-3-12-13",4.4],["16-15-14",2],["16-15",1],null,["16-15-17",2.4],["16-18",1.4],["16-4-2-11-19",4.4],["16-4-2-11-19-20",5.4],["16-4-2-11-19-21",5.8],["16-4-2-11-19-20-22",6.8],["16-4-3-9-23",4.8],["16-4-24",2.4],null,["16-4-1-5-26",5.2],["16-4-1-8-27",4.8]],
[null,["17-14-2-1",3],["17-14-2",2],["17-14-2-3",3.4],["17-14-4",2.4],["17-14-2-5",3.4],["17-14-4-6",3.8],["17-14-2-1-7",4.4],["17-14-2-1-8",4],["17-14-2-3-9",4.8],["17-14-11-10",3.8],["17-14-11",2.4],["17-14-2-1-12",4.4],["17-14-2-1-8-13",5.4],["17-14",1],["17-15",1.4],["17-15-16",2.4],null,["17-18",1],["17-14-11-19",3.4],["17-14-11-19-20",4.4],["17-14-11-19-21",4.8],["17-14-11-19-21-22",5.8],["17-14-2-3-9-23",5.8],["17-15-24",2.8],null,["17-14-2-5-26",4.8],["17-14-2-1-8-27",5]],
[null,["18-14-2-1",3.4],["18-14-2",2.4],["18-15-4-3",3],["18-15-4",2],["18-14-2-5",3.8],["18-15-4-6",3.4],["18-14-2-1-7",4.8],["18-14-2-1-8",4.4],["18-15-4-3-9",4.4],["18-14-11-10",4.2],["18-14-11",2.8],["18-15-4-3-12",4],["18-15-4-3-12-13",5],["18-14",1.4],["18-15",1],["18-16",1.4],["18-17",1],null,["18-14-11-19",3.8],["18-14-11-19-20",4.8],["18-14-11-19-21",5.2],["18-14-11-19-20-22",6.2],["18-15-4-3-9-23",5.4],["18-15-24",2.4],null,["18-14-2-5-26",5.2],["18-14-2-1-8-27",5.4]],
[null,["19-5-1",2.4],["19-11-2",2],["19-5-1-3",3.4],["19-11-2-4",3],["19-5",1.4],["19-5-1-3-6",4.4],["19-5-7",2.4],["19-5-8",2.8],["19-5-1-3-9",4.8],["19-10",1],["19-11",1],["19-5-1-12",3.8],["19-5-8-13",4.2],["19-11-14",2.4],["19-11-2-15",3.4],["19-11-2-4-16",4.4],["19-11-14-17",3.4],["19-11-14-18",3.8],null,["19-20",1],["19-21",1.4],["19-20-22",2.4],["19-5-1-12-23",5.2],["19-11-2-4-24",4],null,["19-10-26",2],["19-5-7-27",3.8]],
[null,["20-10-5-1",3.4],["20-19-11-2",3],["20-10-5-1-3",4.4],["20-19-11-2-4",4],["20-10-5",2.4],["20-10-5-1-3-6",5.4],["20-10-7",2.8],["20-10-5-8",3.8],["20-10-5-1-12-9",5.8],["20-10",1.4],["20-19-11",2],["20-10-5-1-12",4.8],["20-10-5-8-13",5.2],["20-19-11-14",3.4],["20-19-11-2-15",4.4],["20-19-11-2-4-16",5.4],["20-19-11-14-17",4.4],["20-19-11-14-18",4.8],["20-19",1],null,["20-21",1],["20-22",1.4],["20-10-5-1-12-23",6.2],["20-19-11-2-4-24",5],null,["20-10-26",2.4],["20-10-7-27",4.2]],
[null,["21-19-5-1",3.8],["21-19-11-2",3.4],["21-19-5-1-3",4.8],["21-19-11-2-4",4.4],["21-19-5",2.8],["21-19-5-1-3-6",5.8],["21-19-5-7",3.8],["21-19-5-8",4.2],["21-19-5-1-3-9",6.2],["21-19-10",2.4],["21-19-11",2.4],["21-19-5-1-12",5.2],["21-19-5-8-13",5.6],["21-19-11-14",3.8],["21-19-11-2-15",4.8],["21-19-11-2-4-16",5.8],["21-19-11-14-17",4.8],["21-19-11-14-18",5.2],["21-19",1.4],["21-20",1],null,["21-22",1],["21-19-5-1-12-23",6.6],["21-19-11-2-4-24",5.4],null,["21-19-10-26",3.4],["21-19-5-7-27",5.2]],
[null,["22-20-10-5-1",4.8],["22-20-19-11-2",4.4],["22-20-10-5-1-3",5.8],["22-20-19-11-2-4",5.4],["22-20-10-5",3.8],["22-20-10-5-1-3-6",6.8],["22-20-10-7",4.2],["22-20-10-5-8",5.2],["22-20-10-5-1-12-9",7.2],["22-20-10",2.8],["22-20-19-11",3.4],["22-20-10-5-1-12",6.2],["22-20-10-5-8-13",6.6],["22-20-19-11-14",4.8],["22-20-19-11-2-15",5.8],["22-20-19-11-2-4-16",6.8],["22-20-19-11-14-17",5.8],["22-20-19-11-14-18",6.2],["22-20-19",2.4],["22-20",1.4],["22-21",1],null,["22-20-10-5-1-12-23",7.6],["22-20-19-11-2-4-24",6.4],null,["22-20-10-26",3.8],["22-20-10-7-27",5.6]],
[null,["23-12-1",2.8],["23-9-3-2",3.8],["23-9-3",2.4],["23-9-3-4",3.4],["23-12-1-5",3.8],["23-9-6",2],["23-12-8-7",3.4],["23-12-8",2.4],["23-9",1],["23-12-1-5-10",4.8],["23-12-1-11",4.2],["23-12",1.4],["23-13",1],["23-9-3-2-14",4.8],["23-9-3-4-15",4.4],["23-9-3-4-16",4.8],["23-9-3-2-14-17",5.8],["23-9-3-4-15-18",5.4],["23-12-1-5-19",5.2],["23-12-1-5-10-20",6.2],["23-12-1-5-19-21",6.6],["23-12-1-5-10-20-22",7.6],null,["23-9-6-24",3],null,["23-12-8-7-26",4.4],["23-13-27",2]],
[null,["24-3-1",2.4],["24-4-2",2],["24-3",1.4],["24-4",1],["24-3-1-5",3.4],["24-6",1],["24-3-1-7",3.8],["24-3-8",2.8],["24-6-9",2],["24-3-1-5-10",4.4],["24-4-2-11",3],["24-3-12",2.4],["24-6-9-13",3.4],["24-4-14",2.4],["24-15",1.4],["24-4-16",2.4],["24-15-17",2.8],["24-15-18",2.4],["24-4-2-11-19",4],["24-4-2-11-19-20",5],["24-4-2-11-19-21",5.4],["24-4-2-11-19-20-22",6.4],["24-6-9-23",3],null,null,["24-3-1-5-26",4.8],["24-3-8-27",3.8]],
[],
[null,["26-5-1",2.4],["26-5-2",2.8],["26-5-1-3",3.4],["26-5-1-4",3.8],["26-5",1.4],["26-5-1-3-6",4.4],["26-7",1],["26-7-8",2],["26-7-8-12-9",4],["26-10",1],["26-5-11",2.4],["26-7-8-12",3],["26-7-8-13",3.4],["26-5-2-14",3.8],["26-5-2-15",4.2],["26-5-1-4-16",5.2],["26-5-2-14-17",4.8],["26-5-2-14-18",5.2],["26-10-19",2],["26-10-20",2.4],["26-10-19-21",3.4],["26-10-20-22",3.8],["26-7-8-12-23",4.4],["26-5-1-3-24",4.8],null,null,["26-7-27",2.4]],
[null,["27-8-1",2],["27-8-1-2",3],["27-8-3",2.4],["27-8-1-4",3.4],["27-7-5",2.4],["27-12-6",2.8],["27-7",1.4],["27-8",1],["27-12-9",2.4],["27-7-10",2.8],["27-7-5-11",3.4],["27-12",1.4],["27-13",1],["27-8-1-2-14",4],["27-8-1-2-15",4.4],["27-8-1-4-16",4.8],["27-8-1-2-14-17",5],["27-8-1-2-14-18",5.4],["27-7-5-19",3.8],["27-7-10-20",4.2],["27-7-5-19-21",5.2],["27-7-10-20-22",5.6],["27-13-23",2],["27-8-3-24",3.8],null,["27-7-26",2.4]]];
// Номера, названия и координаты секторов
const locations = {
    1: [50,50,"Empire Capital","EmC","Столица Империи"],
    2: [51,50,"East River","EsR","Восточная Река"],
    3: [50,49,"Tiger Lake","TgL","Тигриное Озеро"],
    4: [51,49,"Rogues' Wood","RgW","Лес Разбойников"],
    5: [50,51,"Wolf Dale","WoD","Долина Волков"],
    6: [50,48,"Peaceful Camp","PcC","Мирный Лагерь"],
    7: [49,51,"Lizard Lowland","LzL","Равнина Ящеров"],
    8: [49,50,"Green Wood","GrW","Зеленый Лес"],
    9: [49,48,"Eagle Nest","EgN","Орлиное Гнездо"],
    10: [50,52,"Portal Ruins","PoR","Руины Портала"],
    11: [51,51,"Dragons' Caves","DrC","Пещеры Драконов"],
    12: [49,49,"Shining Spring","ShS","Сияющий Родник"],
    13: [48,49,"Sunny City","SnC","Солнечный Город"],
    14: [52,50,"Magma Mines","MgM","Магма Шахты"],
    15: [52,49,"Bear Mountain","BrM","Медвежья Гора"],
    16: [52,48,"Fairy Trees","FrT","Магический Лес"],
    17: [53,50,"Harbour City","HrC","Портовый Город"],
    18: [53,49,"Mythril Coast","MfC","Мифриловый Берег"],
    19: [51,52,"Great Wall","GtW","Великая Стена"],
    20: [51,53,"Titans' Valley","TiV","Равнина Титанов"],
    21: [52,53,"Fishing Village","FsV","Рыбачье село"],
    22: [52,54,"Kingdom Castle","KiC","Замок Королевства"],
    23: [48,48,"Ungovernable Steppe","UnS","Непокорная Степь"],
    24: [51,48,"Crystal Garden","CrG","Кристальный Сад"],
    25: [53,52,"East Island","EsI","Восточный Остров"],
    26: [49,52,"The Wilderness","ThW","Дикие земли"],
    27: [48,50,"Sublime Arbor","SbA","Великое Древо"],
    101: [48, 53, "Watchers' guild", "", "Гильдия стражей", { Url: "/task_guild.php", Color: "DarkGoldenRod" }],
    102: [48, 54, "Leaders' Guild", "", "Гильдия лидеров", { Url: "/leader_guild.php", Color: "DarkGoldenRod" }],
    103: [49, 54, "Adventurers' guild", "", "Гильдия искателей", { Url: "/campaign_list.php", Color: "DarkGoldenRod" }],
    104: [50, 54, "Thieves' guild", "", "Гильдия воров", { Url: "/thief_guild.php", Color: "DarkGoldenRod" }],
    105: [51, 54, "Smith", "", "Кузница", { Url: "/mod_workbench.php?type=repair", Color: "DarkGoldenRod" }]
};
const objectLocations = { 0 : 0
, 5: 1, 9: 1, 6: 1, 7: 1, 4: 1, 3: 1, 8: 1, 165: 1, 10: 1, 11: 1, 12: 1, 32: 1, 34: 1, 38: 1
, 238: 2, 300: 2, 75: 2, 26: 2, 258: 2, 279: 2, 25: 2, 23: 2, 33: 2, 342: 2, 24: 2, 36: 2, 87: 2, 89: 2, 321: 2, 28: 2
, 239: 3, 16: 3, 15: 3, 13: 3, 259: 3, 343: 3, 280: 3, 84: 3, 301: 3, 224: 3, 27: 3, 39: 3, 14: 3, 31: 3, 35: 3, 322: 3
, 344: 4, 281: 4, 302: 4, 260: 4, 19: 4, 21: 4, 22: 4, 323: 4, 18: 4, 20: 4, 30: 4, 37: 4, 78: 4, 90: 4, 225: 4, 240: 4
, 282: 5, 74: 5, 226: 5, 43: 5, 44: 5, 303: 5, 85: 5, 45: 5, 46: 5, 47: 5, 48: 5, 86: 5, 241: 5, 261: 5, 324: 5, 345: 5
, 50: 6, 283: 6, 262: 6, 304: 6, 73: 6, 49: 6, 141: 6, 51: 6, 79: 6, 53: 6, 54: 6, 55: 6, 52: 6, 82: 6, 325: 6, 346: 6
, 56: 7, 326: 7, 59: 7, 64: 7, 284: 7, 58: 7, 60: 7, 61: 7, 63: 7, 80: 7, 83: 7, 242: 7, 263: 7, 57: 7, 305: 7, 347: 7
, 285: 8, 243: 8, 68: 8, 88: 8, 69: 8, 67: 8, 71: 8, 72: 8, 76: 8, 77: 8, 81: 8, 70: 8, 264: 8, 306: 8, 327: 8, 348: 8
, 244: 9, 328: 9, 227: 9, 265: 9, 286: 9, 94: 9, 349: 9, 139: 9, 307: 9, 101: 9, 98: 9, 95: 9, 119: 9, 120: 9, 140: 9, 97: 9
, 163: 10, 329: 10, 350: 10, 99: 10, 100: 10, 102: 10, 118: 10, 211: 10, 217: 10, 287: 10, 308: 10, 93: 10, 228: 10, 266: 10, 245: 10, 92: 10
, 167: 11, 169: 11, 168: 11, 210: 11, 172: 11, 170: 11, 209: 11, 171: 11, 218: 11, 229: 11, 246: 11, 267: 11, 288: 11, 309: 11, 330: 11, 351: 11
, 247: 12, 331: 12, 289: 12, 219: 12, 117: 12, 108: 12, 111: 12, 110: 12, 109: 12, 112: 12, 113: 12, 114: 12, 230: 12, 268: 12, 310: 12, 352: 12
, 104: 13, 311: 13, 269: 13, 220: 13, 116: 13, 248: 13, 332: 13, 106: 13, 107: 13, 115: 13, 213: 13, 231: 13, 103: 13, 290: 13, 105: 13, 353: 13
, 270: 14, 122: 14, 312: 14, 249: 14, 216: 14, 333: 14, 164: 14, 291: 14, 121: 14, 135: 14, 142: 14, 143: 14, 144: 14, 145: 14, 232: 14, 354: 14
, 313: 15, 334: 15, 124: 15, 125: 15, 214: 15, 136: 15, 123: 15, 146: 15, 147: 15, 148: 15, 149: 15, 215: 15, 250: 15, 271: 15, 292: 15, 355: 15
, 335: 16, 233: 16, 272: 16, 251: 16, 126: 16, 134: 16, 153: 16, 151: 16, 127: 16, 152: 16, 150: 16, 212: 16, 221: 16, 293: 16, 314: 16, 356: 16
, 131: 17, 252: 17, 273: 17, 315: 17, 294: 17, 336: 17, 162: 17, 161: 17, 160: 17, 132: 17, 133: 17, 222: 17, 234: 17, 158: 17, 159: 17, 357: 17
, 358: 18, 235: 18, 253: 18, 130: 18, 129: 18, 137: 18, 138: 18, 154: 18, 128: 18, 155: 18, 156: 18, 157: 18, 274: 18, 295: 18, 316: 18, 337: 18
, 192: 19, 202: 19, 179: 19, 173: 19, 193: 19, 194: 19, 195: 19, 201: 19, 178: 19, 203: 19, 254: 19, 275: 19, 296: 19, 317: 19, 338: 19, 359: 19
, 191: 20, 176: 20, 177: 20, 187: 20, 188: 20, 189: 20, 190: 20, 206: 20, 207: 20, 208: 20, 255: 20, 276: 20, 297: 20, 318: 20, 339: 20, 360: 20
, 174: 21, 199: 21, 197: 21, 166: 21, 361: 21, 198: 21, 196: 21, 200: 21, 223: 21, 236: 21, 256: 21, 277: 21, 298: 21, 319: 21, 340: 21, 175: 21
, 186: 22, 184: 22, 320: 22, 185: 22, 183: 22, 362: 22, 182: 22, 180: 22, 204: 22, 205: 22, 237: 22, 257: 22, 278: 22, 299: 22, 341: 22, 181: 22
, 365: 23, 364: 23, 370: 23, 363: 23, 369: 23, 366: 23, 371: 23, 372: 23, 373: 23, 374: 23, 375: 23, 376: 23, 377: 23, 378: 23, 379: 23, 380: 23
, 367: 24, 392: 24, 390: 24, 388: 24, 383: 24, 384: 24, 385: 24, 386: 24, 387: 24, 368: 24, 389: 24, 381: 24, 391: 24, 382: 24, 393: 24, 394: 24
, 395: 26, 409: 26, 399: 26, 397: 26, 398: 26, 400: 26, 401: 26, 402: 26, 403: 26, 404: 26, 405: 26, 406: 26, 407: 26, 408: 26, 396: 26, 410: 26
, 411: 27, 419: 27, 413: 27, 414: 27, 415: 27, 416: 27, 417: 27, 418: 27, 412: 27, 420: 27, 421: 27, 422: 27, 423: 27, 424: 27, 425: 27, 426: 27
 };

const minX = 48;
const maxX = 53;
const minY = 48;
const maxY = 54;
const DefaultTravelingTime = 40;
let AmbushMinutesInterval = 60;
const ClientServerTimeDifferenceRequestInterval = 60 * 60 * 1000;
const isMobileInterface = document.querySelector("div#btnMenuGlobal") ? true : false;
const GoTo = isEn ? "Go to" : "Перейти в";
const Second = isEn ? "sec." : "с.";
const YourPremiumMountUntil = isEn ? "Your premium mount  until" : "Ваш премиум транспорт";
const YourMountUntil = isEn ? "Your mount until" : "Ваш транспорт до";
const Until = isEn ? " until " : " до ";
const UndressBeforeMove = isEn ? "Undress before move" : "Раздеться перед перемещением";
const NotUseComplexRoute = isEn ? "Not use complex route" : "Не использовать сложный маршрут";
const HideMapName = isEn ? "Hide map" : "Скрыть карту";
const HideHuntName = isEn ? "Hide hunt" : "Скрыть охоты";
const HideRightBlockName = isEn ? "Hide right block" : "Скрыть правый блок";
const ComplexRouteName = isEn ? "Complex route" : "Сложный маршрут";
const MountUntilName = isEn ? "Mount until" : "Транспорт до";
const BreakTravelingName = isEn ? "Break traveling" : "Прервать путешествие";
const TravelingNotEnabled = isEn ? "Traveling not enabled" : "Путешествие не доступно";
const YouAreInAChallenge = isEn ? "You are in a challenge. Your actions are limited!" : "Вы находитесь в заявке на бой. Ваши действия ограничены!";
const How = isEn ? "How?" : "Как?";
const YouAreHere = isEn ? "You're already here" : "Ты уже здесь";
const AmbushMaySettedOnAdjacentSector = isEn ? "Ambush may setted on adjacent sector" : "Засада ставится на соседний сектор";
const ItIsTooEarly = isEn ? "It is too early" : "Ещё рано";
const Legend = isEn ? "Click: moving, ctrl(mdl)+click: ambush, alt+click: sector info" : "Click: движение, ctrl(mdl)+click: засада, alt+click: просмотр";
const MoveHere = isEn ? "Move here" : "Перейти сюда";
const YouAreInADifferentLocation = isEn ? "You are in a different location." : "Вы находитесь в другом районе.";
const TravelingTimeName = isEn ? "Traveling time" : "Время пути";
const Antithief = isEn ? "Antithief" : "Антивор";
const AmbushingName = isEn ? "You are in ambush" : "Вы в засаде";
const CheckMountName = isEn ? "Check mount" : "Проверить транспорт";
const LoadName = isEn ? "Load" : "Грузить";
const PremiunAccountName = isEn ? "Abu" : "Абу";
const locationNamesLocalizedColumn = isEn ? 2 : 4;
const AmbushResult = { NotFound: 0, Win: 1, Fail: 2 };
const factoryTypes = ["mn", "fc", "sh"];
const Mining = isEn ? "Mining" : "Добыча";
const Machining = isEn ? "Machining" : "Обработка";
const Production = isEn ? "Production" : "Производство";
const PopupAlwaysName = isEn ? "Popup always" : "Всегда всплывающая";
const HideBattlesDayWithName = isEn ? "Hide battles day with..." : "Скрыть день боёв с...";
const FindNearestMiningName = isEn ? "Find nearest mining" : "Найти ближайшую добычу";
const FindNearestManufactureName = isEn ? "Find nearest manufacture" : "Найти ближайшую обработку";
const FindNearestProductionName = isEn ? "Find nearest production" : "Найти ближайшее производство";
const MoveToWorkAfterFindName = isEn ? "Move to work after find" : "Сразу идти на предприятие";
const ShowLocationNumbersName = isEn ? "Show location numbers" : "Номера локаций";
const JobFinding = isEn ? "Job finding" : "Поиск работы";

const windowObject = window.wrappedJSObject || unsafeWindow;

let persGvStopped = false;
let playerLocationNumber = getPlayerLocationNumber();
if(!playerLocationNumber) {
    return; // При первом запуске скрипта, если мы не на карте, неизвестна текущая локация игрока
}
let MountInfo;
var AmbushMoratoriumPanel;

main();
async function main(forceGetMountInfo) {
    initUserName();
    MountInfo = await getMountInfo(forceGetMountInfo);
    AmbushMinutesInterval *= 1 - (MountInfo.IsPremiunAccount ? 0.3 : 0);

    tryGetAmbushState();
    if(!isHeartOnPage) {
        return;
    }
    healthTimer();
    requestServerTime();
    checkActivity();
    setActivity();
    if(/map.php/.test(location.href)) {
        const mapRightBlock = document.getElementById("map_right_block");
        if(!mapRightBlock) {
            drawStopButton();
            return; // Нет правого блока с предприятиями, значит мы в пути. Скрипт не выполняем
        }
        GM_setValue("LastObservingLocationNumber", location.search != '' ? getLocationNumberFromMapUrlByXy(location.href) : playerLocationNumber);
        loadFactories();
    }
    drawMap();
    processMoving();
    checkAmbushResult();
    if(location.pathname == '/object-info.php') {
        const center = document.querySelector("body center");
        const objectLocationReference = center.querySelector("a[href*='map.php?cx='][href*='&cy=']");
        //console.log(`center: ${center}, objectLocationReference: ${objectLocationReference}`);
        const moveHereReference = getMoveToObjectReference(location.pathname + location.search, objectLocationReference); // Добавление кнопки перемещения на предприятие
    }
    if(location.pathname == '/ecostat_details.php' || location.pathname == '/home.php') {
        const objs = document.querySelectorAll("a[href*='object-info.php?id=']");
        const doubledHash = [];
        for(let obj of objs) {
            if(!doubledHash.includes(obj.href)) {
                const moveHereReference = getMoveToObjectReference(obj.href, obj);
                doubledHash.push(obj.href);
            }
        }
    }
    if(location.pathname == '/group_wars.php') {
        addMoveToMapObjectReferences();
    }
    if(location.pathname == '/mercenary_guild.php') {
        const colorElement = document.querySelector("font[color='#E65054']");
        if(colorElement.querySelector("b").innerText == YouAreInADifferentLocation) {
            const lastObservingLocationNumber = parseInt(GM_getValue("LastObservingLocationNumber", 0));
            if([2, 6, 16, 21].includes(lastObservingLocationNumber)) {
                const ref = getMoveToMapObjectReference(location.href, colorElement, lastObservingLocationNumber);
            } else {
                const nearestLocations = getLocationsSortedByDistance();
                //console.log(`nearestLocations: ${nearestLocations}`);
                for(const nearestLocation of nearestLocations) {
                    if([2, 6, 16, 21].includes(parseInt(nearestLocation))) {
                        const ref = getMoveToMapObjectReference(location.href, colorElement, nearestLocation);
                        break;
                    }
                }
            }
        }
    }
    if(location.pathname == '/pirate_event.php') {
        const tableDiv = document.getElementById("tableDiv");
        addMoveToMapObjectReferences(tableDiv);
    }
    processHouseInfo();
}
function checkActivity() {
    checkTurnedOn();
    setTimeout(checkActivity, 1000);
}
function setActivity() {
    checkTurnedOn();
    GM_setValue("LastActivityDate", Date.now());
    setTimeout(setActivity, 60 * 1000);
}
function checkTurnedOn() {
    const now = Date.now();
    const computerTurnedOn = parseInt(GM_getValue("LastActivityDate", 0)) + 5 * 60 * 1000 < now;
    //console.log(`now: ${now}, LastActivityDate: ${GM_getValue("LastActivityDate")}, computerTurnedOn: ${computerTurnedOn}`);
    if(computerTurnedOn) {
        //console.log(`now: ${now}, LastActivityDate: ${GM_getValue("LastActivityDate")}, computerTurnedOn: ${computerTurnedOn}`);
        checkAmbushResult(true);
    }
}
function addMoveToMapObjectReferences(element) {
    element = element || document;
    const locationRefs = element.querySelectorAll("a[href^='map.php?cx=']");
    for(let locationRef of locationRefs) {
        const locationNumber = getLocationNumberByCoordinate(getUrlParamValue(locationRef.href, "cx"), getUrlParamValue(locationRef.href, "cy"));
        const ref = getMoveToMapObjectReference(location.href, locationRef, locationNumber);
    }
}
function getObjectLocations() {
    const objs = document.querySelectorAll("a[href*='object-info.php?id=']");
    let absentList = "";
    for(let obj of objs) {
        const objectId = getUrlParamValue(obj.href, "id");
        if(!objectLocations[objectId]) {
            objectLocations[objectId] = playerLocationNumber;
            absentList += `, ${objectId}: ${playerLocationNumber}`;
        }
    }
    if(absentList != "") {
        ShowBigData(absentList);
    }
    //console.log(absentList);
}
function drawStopButton() {
    const mapRightBlock = document.getElementById("map_right_block");
    //console.log([MountInfo.ComplexRoute, GM_getValue("IgnoreComplexRoute"), GM_getValue("IgnoreComplexRoute") == "true", (!MountInfo.ComplexRoute) || (GM_getValue("IgnoreComplexRoute") == "true")]);
    if(!mapRightBlock && GM_getValue("TargetLocationNumber") && (!MountInfo.ComplexRoute || gmGetBool("IgnoreComplexRoute"))) {
        let targetLocationNumber = parseInt(GM_getValue("TargetLocationNumber"));
        let route = routes[playerLocationNumber][targetLocationNumber][0];
        let nextLocationNumber = route.split('-')[1];
        if(nextLocationNumber != targetLocationNumber) {
            const insideMap = document.querySelector("#inside_map");
            const mapMoving = addElement("div", insideMap, { id: "mapMoving" });
            const stopMovingButton = addElement("div", mapMoving, { id: "stopMovingButton", class: "home_button2 btn_hover2 map_btn_width", style: "width: 300px;", innerHTML: BreakTravelingName });
            stopMovingButton.addEventListener("click", stopMoving, false);
        }
    }
}
async function toggleAntithief(toggleAntithiefCheckbox) {
    const toggleKey = GM_getValue("ToggleAntithiefKey");
    if(toggleKey && MountInfo.AntithiefControlled) {
        const toggleVar = toggleAntithiefCheckbox.checked ? "t_on" : "t_off";
        //console.log(`toggleKey: ${toggleKey}, url: /shop.php?${toggleVar}=${toggleKey}&cat=transport`);
        await getRequest(`/shop.php?${toggleVar}=${toggleKey}&cat=transport`);
    }
}
function toggleHuntBlock() {
    const map_hunt_block_div = document.getElementById("map_hunt_block_div");
    if(map_hunt_block_div) {
        const mapRightBlock = document.getElementById("map_right_block");
        let mapRightBlockHeight = mapRightBlock.offsetHeight;
        if(gmGetBool("HideHuntBlock")) {
            mapRightBlockHeight -= map_hunt_block_div.offsetHeight;
        }
        map_hunt_block_div.style.display = gmGetBool("HideHuntBlock") ? "none" : "block";
        if(!gmGetBool("HideHuntBlock")) {
            mapRightBlockHeight += map_hunt_block_div.offsetHeight;
        }
        mapRightBlock.style.height = `${mapRightBlockHeight}px`;
    }
}
function toggleBattlesDayWith() {
    const mapRightBlock = document.querySelector("#map_right_block");
    if(mapRightBlock) {
        const battlesDayTable = mapRightBlock.querySelector("table.wbwhite");
        if(battlesDayTable && !battlesDayTable.classList.contains("rounded_table")) {
            battlesDayTable.style.display = gmGetBool("HideBattlesDayWithBlock") ? "none" : "";
        }
    }
}
function toggleRightBlock() {
    const mapRightBlock = document.getElementById("map_right_block");
    if(mapRightBlock) {
        mapRightBlock.style.display = gmGetBool("HideRightBlock") ? "none" : "block";
    }
}
function tryGetAmbushState() {
    if(location.pathname == '/map.php') {
        const ambushDiv = Array.from(document.querySelectorAll("div#map_right_block .wbwhite.rounded_table")).find(x => x.innerHTML.includes(AmbushingName));
        if(ambushDiv) {
            GM_setValue("AmbushState", AmbushState.Setted);
            GM_deleteValue("AmbushSuspendExpireDate");
        } else {
            if(GM_getValue("AmbushState") == AmbushState.Setted) {
                GM_deleteValue("AmbushState");
            }
        }
    }
    if(location.pathname == '/war.php') {
        const warId = getUrlParamValue(location.href, "warid");
        const lt = getUrlParamValue(location.href, "lt");
        finalResultDiv = document.getElementById("finalresult_text");
        if(lt != "-1" && finalResultDiv.innerHTML.length <= 10 && GM_getValue("AmbushState") == AmbushState.Setted) {
            GM_setValue("AmbushState", AmbushState.Battle);
            observe(finalResultDiv, parseBattleResultPanel);
        }
    }
    console.log(`tryGetAmbushState AmbushState: ${GM_getValue("AmbushState")}`);
}
function parseBattleResultPanel() {
    if(GM_getValue("AmbushState") == AmbushState.Battle && finalResultDiv.innerHTML.length > 10) {
        GM_deleteValue("AmbushState");
        const bolds = finalResultDiv.querySelectorAll("font b");
        let result = "fail";
        for(const bold of bolds) {
            if(bold.innerHTML == (isEn ? "Victorious:" : "Победившая сторона:")) {
                console.log(bold.parentNode.nextSibling.nextSibling.firstChild.innerText);
                if(bold.parentNode.nextSibling.nextSibling.firstChild.innerText == GM_getValue("TransporterUserName")) {
                    result = "win";
                }
                break;
            }
        }
        if(result == "fail") {
            GM_setValue("AmbushSuspendExpireDate", Date.now() + AmbushMinutesInterval * 60 * 1000);
        }
        console.log(`result: ${result}, AmbushSuspendExpireDate: ${GM_getValue("AmbushSuspendExpireDate")}`);
    }
}
async function checkAmbushResult(force = false) {
    console.log(`checkAmbushResult force: ${force}, AmbushState: ${GM_getValue("AmbushState")}, AmbushSuspendExpireDate: ${GM_getValue("AmbushSuspendExpireDate")}`);
    const afterAmbushUnknownResult = GM_getValue("AmbushState") == AmbushState.Battle; // После засады был бой
    if(afterAmbushUnknownResult && !isFullHealth() && !force) {
        GM_setValue("AmbushSuspendExpireDate", Date.now() + AmbushMinutesInterval * 60 * 1000);
    } else {
        let doc;
        if(location.pathname == '/pl_warlog.php') {
            const page = getUrlParamValue(location.href, "page");
            const id = getUrlParamValue(location.href, "id");
            if(id == PlayerId && (!page || page == 0)) {
                doc = document;
            }
        }
        if(doc || afterAmbushUnknownResult || force) {
            doc = doc || await getRequest(`/pl_warlog.php?id=${PlayerId}`);
            processWarlog(doc);
        }
    }
    if(afterAmbushUnknownResult) {
        GM_deleteValue("AmbushState");
    }
    refreshAmbushMoratoriumPanel();
}
function getHealth() {
    const health_amount = document.getElementById("health_amount");
    let health;
    if(health_amount) {
        health = parseInt(health_amount.innerText);
    } else {
        health = windowObject.heart;
    }
    return health;
}
function isFullHealth() {
    const healthRestoreTime = parseInt(GM_getValue("HealthRestoreTime", 0));
    return healthRestoreTime < Date.now();
}
function healthTimer() {
    if(isHeartOnPage) {
        const health_amount = document.getElementById("health_amount");
        let heart; // 78
        let maxHeart; // 100
        let timeHeart; // 405
        if(health_amount) {
            const res = /top_line_draw_canvas_heart\((\d+), (\d+), (\d+)\);/.exec(document.body.innerHTML); // top_line_draw_canvas_heart(0, 100, 405);
            if(res) {
                heart = parseInt(res[1]);
                maxHeart = parseInt(res[2]);
                timeHeart = parseInt(res[3]);
            }
        } else {
            heart = windowObject.heart;
            maxHeart = windowObject.max_heart;
            timeHeart = windowObject.time_heart;
        }
        console.log(`healthTimer heart: ${heart}, maxHeart: ${maxHeart}, timeHeart: ${timeHeart}`);
        let restSeconds = timeHeart * (maxHeart - heart) / maxHeart;
        if(restSeconds > 0) {
            GM_setValue("HealthRestoreTime", Date.now() + restSeconds * 1000);
        } else {
            GM_deleteValue("HealthRestoreTime");
        }
    }
    expireHealthRestoreTime();
}
function expireHealthRestoreTime() {
    const healthRestoreTime = parseInt(GM_getValue("HealthRestoreTime", 0));
    //console.log(`expireHealthRestoreTime healthRestoreTime: ${healthRestoreTime}}`);
    if(healthRestoreTime > 0) {
        if(Date.now() > healthRestoreTime) {
            GM_deleteValue("HealthRestoreTime");
            drawAmbushMarks();
        } else {
            setTimeout(expireHealthRestoreTime, 1000);
        }
    }
}
function processWarlog(doc) {
    const lastAmbushRef = Array.from(doc.querySelectorAll("a[href*='warlog.php?warid=']")).find(x => x.nextSibling.textContent == ": • ");
    let lastAmbushResult = AmbushResult.NotFound;
    if(lastAmbushRef) {
        lastAmbushResult = lastAmbushRef.nextElementSibling.tagName.toLowerCase() == "b" ? AmbushResult.Win : AmbushResult.Fail;
        var lastAmbushDate = toClientTime(parseDate(lastAmbushRef.innerText));
    }
    if(lastAmbushDate) {
        if(lastAmbushResult == AmbushResult.Fail) {
            const newAmbushSuspendExpireDate = lastAmbushDate.getTime() + (AmbushMinutesInterval + 1) * 60 * 1000;
            // Если нет старого значения AmbushSuspendExpireDate или расхождение с новым из лога больше минуты, то установим новое, если оно ещё актуально
            if(!GM_getValue("AmbushSuspendExpireDate") || Math.abs(parseInt(GM_getValue("AmbushSuspendExpireDate")) - newAmbushSuspendExpireDate) > 60 * 1000) {
                if(newAmbushSuspendExpireDate > Date.now()) {
                    GM_setValue("AmbushSuspendExpireDate", newAmbushSuspendExpireDate);
                } else {
                    GM_deleteValue("AmbushSuspendExpireDate");
                }
            }
        }
    } else {
        GM_deleteValue("AmbushSuspendExpireDate");
    }
    console.log(`processWarlog lastAmbushResult: ${lastAmbushResult}, lastAmbushDate: ${lastAmbushDate.toLocaleString()}, ambushSuspendExpireDate: ${fromTimeSpanToString(GM_getValue("AmbushSuspendExpireDate"))}`);
}
function fromTimeSpanToString(timeSpan) {
    if(timeSpan) {
        return (new Date(parseInt(timeSpan))).toLocaleString();
    }
}
function toClientTime(serverDateTime) {
    //console.log(`LastRequestedClientServerTimeDifferenceMilliseconds: ${GM_getValue("LastRequestedClientServerTimeDifferenceMilliseconds", 0)}, serverDateTime.getTime(): ${serverDateTime.getTime()}, ${serverDateTime.getTime() + GM_getValue("LastRequestedClientServerTimeDifferenceMilliseconds", 0)}`);
    return new Date(serverDateTime.getTime() + parseInt(GM_getValue("LastRequestedClientServerTimeDifferenceMilliseconds", 0)));
}
function getServerTime() { return Date.now() + parseInt(GM_getValue("LastRequestedClientServerTimeDifferenceMilliseconds", 0)); }
function requestServerTime() {
    if(parseInt(GM_getValue("LastClientServerTimeDifferenceRequestDate", 0)) + ClientServerTimeDifferenceRequestInterval < (new Date().getTime())) {
        GM_setValue("LastClientServerTimeDifferenceRequestDate", new Date().getTime());
        const objXMLHttpReqTime = new XMLHttpRequest();
        objXMLHttpReqTime.open('GET', '/time.php?rand=' + (Math.random() * 1000000), true);
        objXMLHttpReqTime.onreadystatechange = function() {
            if(objXMLHttpReqTime.readyState == 4 && objXMLHttpReqTime.status == 200) {
                let responseParcing = /now (\d+)/.exec(objXMLHttpReqTime.responseText); //objXMLHttpReqTime.responseText: now 1681711364 17-04-23 09:02
                if(responseParcing) {
                    GM_setValue("LastRequestedClientServerTimeDifferenceMilliseconds", new Date().getTime() - parseInt(responseParcing[1]) * 1000);
                }
            }
        }
        objXMLHttpReqTime.send(null);
    } else {
        setTimeout(function() { requestServerTime(); }, ClientServerTimeDifferenceRequestInterval);
    }
}
function truncDateTimeToMinutes(dateTime) {
    const coeff = 1000 * 60;
    return new Date(Math.floor(dateTime.getTime() / coeff) * coeff);
}
function refreshAmbushMoratoriumPanel() {
    if(GM_getValue("AmbushSuspendExpireDate")) {
        const ambushMoratoriumEndDate = parseInt(GM_getValue("AmbushSuspendExpireDate"));
        const now = Date.now();
        //console.log(`ambushMoratoriumEndDate > now: ${ambushMoratoriumEndDate > now}`);
        if(ambushMoratoriumEndDate > now) {
            const formatedTime = formatInterval(ambushMoratoriumEndDate - now);
            setAmbushMoratoriumPanelValue(formatedTime);
            
            const pers_gv = refreshAmbushMoratoriumPanel.pers_gv || (refreshAmbushMoratoriumPanel.pers_gv = document.querySelector("#pers_gv"));
            persGvStopped = persGvStopped || pers_gv && pers_gv.innerText == "00:00" && formatedTime != "00:00";
            if(persGvStopped) {
                pers_gv.innerText = formatedTime;
            }
            setTimeout(refreshAmbushMoratoriumPanel, 1000);
        } else {
            setAmbushMoratoriumPanelValue();
            GM_deleteValue("AmbushSuspendExpireDate");
            if(gmGetBool("ThievesGuildNotifications")) {
                GM.notification("Вы можете устроить засаду", "ГВД", "https://dcdn.heroeswm.ru/i/portraits/thiefwarrioranip33.png", function() { window.focus(); getURL("/map.php"); });
            }
            persGvStopped = false;
            drawAmbushMarks();
        }
    } else {
        setAmbushMoratoriumPanelValue();
        drawAmbushMarks();
    }
}
function setAmbushMoratoriumPanelValue(value) {
    if(AmbushMoratoriumPanel) {
        AmbushMoratoriumPanel.innerText = value || "";
    }
}
function formatInterval(interval) {
    let diff = interval;
    const hours = Math.floor(diff / 1000 / 60 / 60);
    diff -= hours * 1000 * 60 * 60;
    const mimutes = Math.floor(diff / 1000 / 60);
    diff -= mimutes * 1000 * 60;
    const seconds = Math.floor(diff / 1000);
    const formatedTime = (hours > 0 ? hours + ":" : "") + ( (mimutes < 10) ? '0' : '' ) + mimutes + ':' + (seconds < 10 ? '0' : '') + seconds;
    return formatedTime;
}
function drawMap() {
    const insideMap = document.querySelector("#inside_map");
    const jsmap = document.querySelector("#jsmap");
    const isHideMap = gmGetBool("HideMap");
    const mapRightBlock = document.getElementById("map_right_block");
    if(jsmap) {
        jsmap.style.display = isHideMap ? "none" : "block";
    }
    toggleRightBlock();
    toggleBattlesDayWith();
    toggleHuntBlock();
    if(insideMap && (!gmGetBool("PopupAlways") || isMobileInterface)) {
        mapRightBlock.style.removeProperty('height');
        let mapMovingContainer = insideMap;
        let mapMovingStyle = "";
        if(isMobileInterface) {
            insideMap.style.flexWrap = "wrap";
            //insideMap.style.justifyContent = "normal";
            addElement("div", insideMap, { style: "flex-basis: 100%; height: 0;"});
            mapMovingStyle = "order: 4;";
        } else {
            mapMovingContainer = addElement("div", insideMap, { style: "order: 1;" });
            mapMovingContainer.appendChild(jsmap);
            insideMap.style.gap = "10px";
        }
        drawMapCore(mapMovingContainer, mapMovingStyle);
    } else {
        let mapHeight = 350;
        let mapWidth = 500;
        const mapActivatorDiv = addElement("div", document.body, { style: `position: fixed; width: 5px; height: ${ClientHeight() - 50}px; top: 50px; left: 0; background-color: #FFCCCB; opacity: 80%;` });
        const mapContainerDiv = addElement("div", document.body, { style: `position: fixed; width: ${mapWidth}px; height: ${mapHeight}px; top: ${(ClientHeight() - mapHeight)/ 2}px; left: -${mapWidth}px; transition-duration: 0.3s; z-index: 10;` }, true);
        const mapMoving = drawMapCore(mapContainerDiv, "box-shadow: 1px 1px 5px #333;"); //, `height: ${innerMapHeight}px`
        let timer;
        mapActivatorDiv.addEventListener("mouseover", function(event) { timer = setTimeout(function() { mapContainerDiv.style.top = `${ClientHeight() / 2 - 175}px`; mapContainerDiv.style.left = "0"; }, 100); }, false);
        mapActivatorDiv.addEventListener("mouseout", function(event) { timer = setTimeout(function() { mapContainerDiv.style.left = `-${mapWidth + 10}px`; }, 300); }, false);
        mapContainerDiv.addEventListener("mouseover", function(event) { clearTimeout(timer); }, false);
        mapContainerDiv.addEventListener("mouseout", function(event) { timer = setTimeout(function() { mapContainerDiv.style.left = `-${mapWidth + 10}px`; }, 100); }, false);
    }
}
function drawMapCore(parentNode, mainStyle) {
    let mapMoving = document.getElementById("mapMoving");
    if(mapMoving) {
        mapMoving.parentNode.removeChild(mapMoving);
    }
    const workImageSize = 25;
    mapMoving = addElement("div", parentNode, { id: "mapMoving", style: (mainStyle || "") + "border: 2mm ridge rgba(211, 220, 50, .6); background: linear-gradient(0.15turn, #3f87a6, #ebf8e1, #f69d3c 80%);" });
    mapMoving.innerHTML = `
<div style="position: relative; text-align: center; font-size: 9px;">
    <span style="font-size: 9px;">${ (MountInfo.EndDate ? `${MountInfo.ComplexRoute ? ComplexRouteName + ", " : ""}${MountInfo.IsPremiunAccount ? PremiunAccountName + ", " : ""}${MountUntilName}: ${(new Date(MountInfo.EndDate)).toLocaleString()}, ` : "") + `${TravelingTimeName}: ${MountInfo.TravelingTime} ${Second}` }</span>
    ${ MountInfo.AntithiefControlled ? `, <input id='toggleAntithiefCheckbox' type='checkbox' ${MountInfo.Antithief ? "checked" : ""}>${Antithief}` : "" }
    <div id="checkMountButton" title="${CheckMountName}" style="display: inline-block;">
        <img src="https://dcdn2.heroeswm.ru/i/pl_info/btn_reset.png" style="height: 12px; wigth: 12px;">
    </div>
    <br />
    <input id='removeArtsBeforeMovingCheckbox' type='checkbox' ${gmGetBool("removeArtsBeforeMoving") ? "checked" : ""}>${UndressBeforeMove}
    <input id='IgnoreComplexRouteCheckbox' type='checkbox' ${gmGetBool("IgnoreComplexRoute") ? "checked" : ""}>${NotUseComplexRoute}
    <input id='HideMapCheckbox' type='checkbox' ${gmGetBool("HideMap") ? "checked" : ""}>${HideMapName}
    <br />
    <input id='HideHuntBlockCheckbox' type='checkbox' ${gmGetBool("HideHuntBlock") ? "checked" : ""}>${HideHuntName}
    <input id='HideRightBlockCheckbox' type='checkbox' ${gmGetBool("HideRightBlock") ? "checked" : ""}>${HideRightBlockName}
    <input id='popupAlwaysCheckbox' type='checkbox' ${gmGetBool("PopupAlways") ? "checked" : ""}>${PopupAlwaysName}
    <input id='showLocationNumbersCheckbox' type='checkbox' ${gmGetBool("ShowLocationNumbers") ? "checked" : ""}>${ShowLocationNumbersName}
</div>
<div>
    <table id="hwmMapMoveTable" style="">
    </table>
</div>
<div style="position: relative; text-align: left; font-size: 9px;">
    <div id="findNearestProductionBigButton" title="${FindNearestProductionName}" style="float: right;">
        <img src="https://dcdn.heroeswm.ru/i/btns/job_fl_btn_production.png" style="scale: 1.5;">
    </div>
    ${LoadName}<input id='loadMiningCheckbox' type='checkbox' ${gmGetBool("Load" + factoryTypes[0]) ? "checked" : ""}>${Mining}
    <input id='loadMachiningCheckbox' type='checkbox' ${gmGetBool("Load" + factoryTypes[1]) ? "checked" : ""}>${Machining}
    <input id='loadProductionCheckbox' type='checkbox' ${gmGetBool("Load" + factoryTypes[2]) ? "checked" : ""}>${Production}
    <input id='HideBattlesDayWithBlockCheckbox' type='checkbox' ${gmGetBool("HideBattlesDayWithBlock") ? "checked" : ""}>${HideBattlesDayWithName}
    <br/>
    <span style="font-size: 9px;">${Legend}, PlayerSector: ${playerLocationNumber}${playerLocationNumber != GM_getValue("LastObservingLocationNumber") ? ", View: " + GM_getValue("LastObservingLocationNumber") : ""}</span>
    <!--<span id="ClickMe">ClickMe</span>-->
    <br/>
    ${isEn ? "Min salary: " : "Минимальная зарплата: "}<input id='minSalaryNumber' type='number' value='${GM_getValue("MinSalary", "")}' size="4" onfocus="this.select();">
    <input id='thievesGuildNotificationsCheckbox' type='checkbox' ${gmGetBool("ThievesGuildNotifications") ? "checked" : ""}>${isEn ? "Thieves guild notifications" : "Оповещение о засаде"}
    <br/>
    <b>${JobFinding}:</b>
    <div id="findNearestMiningButton" title="${FindNearestMiningName}" style="display: inline-block;">
        <img src="https://dcdn.heroeswm.ru/i/btns/job_fl_btn_mining.png" style="width: ${workImageSize}px; height: ${workImageSize}px;">
    </div>
    <div id="findNearestManufactureButton" title="${FindNearestManufactureName}" style="display: inline-block;">
        <img src="https://dcdn.heroeswm.ru/i/btns/job_fl_btn_manufacture.png" style="width: ${workImageSize}px; height: ${workImageSize}px;">
    </div>
    <div id="findNearestProductionButton" title="${FindNearestProductionName}" style="display: inline-block;">
        <img src="https://dcdn.heroeswm.ru/i/btns/job_fl_btn_production.png" style="width: ${workImageSize}px; height: ${workImageSize}px;">
    </div>
    <input id='moveToWorkAfterFindCheckbox' type='checkbox' ${gmGetBool("MoveToWorkAfterFind") ? "checked" : ""}>${MoveToWorkAfterFindName}
    <span id="workFindingMessageSpan"/>
</div>`;
    mapMoving.querySelector("#removeArtsBeforeMovingCheckbox").addEventListener("click", function() { GM_setValue("removeArtsBeforeMoving", this.checked); }, false);
    mapMoving.querySelector("#IgnoreComplexRouteCheckbox").addEventListener("click", function() { GM_setValue("IgnoreComplexRoute", this.checked); }, false);
    mapMoving.querySelector("#HideMapCheckbox").addEventListener("click", function() { GM_setValue("HideMap", this.checked); window.location.reload(); }, false);
    if(MountInfo.AntithiefControlled) {
        mapMoving.querySelector("#toggleAntithiefCheckbox").addEventListener("click", function() { toggleAntithief(this); }, false);
    }
    mapMoving.querySelector("#HideHuntBlockCheckbox").addEventListener("click", function() { GM_setValue("HideHuntBlock", this.checked); toggleHuntBlock(); }, false);
    mapMoving.querySelector("#HideRightBlockCheckbox").addEventListener("click", function() { GM_setValue("HideRightBlock", this.checked); toggleRightBlock(); }, false);
    mapMoving.querySelector("#checkMountButton").addEventListener("click", function() { main(true); }, false);
    
    mapMoving.querySelector("#loadMiningCheckbox").addEventListener("click", function() { GM_setValue("Load" + factoryTypes[0], this.checked); }, false);
    mapMoving.querySelector("#loadMachiningCheckbox").addEventListener("click", function() { GM_setValue("Load" + factoryTypes[1], this.checked); }, false);
    mapMoving.querySelector("#loadProductionCheckbox").addEventListener("click", function() { GM_setValue("Load" + factoryTypes[2], this.checked); }, false);
    mapMoving.querySelector("#popupAlwaysCheckbox").addEventListener("click", function() { GM_setValue("PopupAlways", this.checked); window.location.reload(); }, false);
    mapMoving.querySelector("#showLocationNumbersCheckbox").addEventListener("click", function() { GM_setValue("ShowLocationNumbers", this.checked); window.location.reload(); }, false);
    mapMoving.querySelector("#HideBattlesDayWithBlockCheckbox").addEventListener("click", function() { GM_setValue("HideBattlesDayWithBlock", this.checked); toggleBattlesDayWith(); }, false);
    
    //mapMoving.querySelector("#ClickMe").addEventListener("click", function() { getObjectLocations(); }, false);
    
    mapMoving.querySelector("#moveToWorkAfterFindCheckbox").addEventListener("click", function() { GM_setValue("MoveToWorkAfterFind", this.checked); }, false);
    mapMoving.querySelector("#minSalaryNumber").addEventListener("change", function() { GM_setValue("MinSalary", parseInt(this.value)); }, false);
    mapMoving.querySelector("#findNearestMiningButton").addEventListener("click", function() { findNearestWork("mn"); }, false);
    mapMoving.querySelector("#findNearestManufactureButton").addEventListener("click", function() { findNearestWork("fc"); }, false);
    mapMoving.querySelector("#findNearestProductionButton").addEventListener("click", function() { findNearestWork("sh"); }, false);
    mapMoving.querySelector("#findNearestProductionBigButton").addEventListener("click", function() { findNearestWork("sh"); }, false);
    mapMoving.querySelector("#thievesGuildNotificationsCheckbox").addEventListener("click", function() { GM_setValue("ThievesGuildNotifications", this.checked); }, false);

    const table = mapMoving.querySelector("#hwmMapMoveTable");
    const now = new Date();
    for(let y = minY; y <= maxY; y++) {
        const tableRow = addElement("tr", table);
        for(let x = minX; x <= maxX; x++) {
            let cellStyle = "";
            let locationNumber = getLocationNumberByCoordinate(x, y);
            const tableCell = addElement("td", tableRow, { id: `transporterLocation${locationNumber}` });
            if(locationNumber) {
                const loc = locations[locationNumber];
                tableCell.innerHTML = `${loc[locationNamesLocalizedColumn].replace(" ", "<br>")} ${ gmGetBool("ShowLocationNumbers") ? `(${locationNumber})` : "" }`;
                tableCell.style = `cursor: pointer; font-size: 10px; font-weight: bold; text-align: center; border: 1px solid #000; padding: 3px; color: ${locationNumber == playerLocationNumber ? "red" : "black"};`;
                if(locationNumber < 100) {
                    let routeTime = "";
                    const routeSettings = routes[playerLocationNumber][locationNumber];
                    if(routeSettings) {
                        const routeLength = routeSettings[1];
                        routeTime = `\n${TravelingTimeName} ${routeLength * MountInfo.TravelingTime} ${Second}`;
                    }
                    tableCell.title = `${GoTo} ${loc[locationNamesLocalizedColumn]}${routeTime}`;
                    tableCell.addEventListener("mouseenter", function() { this.style.border = "1px solid #fff"; }, false);
                    tableCell.addEventListener("mouseleave", function() { this.style.border = "1px solid #000"; }, false);
                    tableCell.addEventListener("click", function(e) { if(e.altKey) { getURL(`/map.php?cx=${x}&cy=${y}`); } else if(e.ctrlKey) { setThiefAmbush(locationNumber); } else { tryMoving(locationNumber); } }, false);
                    tableCell.addEventListener("auxclick", function(e) { e.stopPropagation();/*if(e.which === 2) { e.preventDefault(); }*/ if(e.button == 1) { setThiefAmbush(locationNumber); } }, false); // console.log(`e.which: ${e.which}, e.button: ${e.button}`);
                    const sectorViewReference = addElement("a", tableCell, { href: `/map.php?cx=${x}&cy=${y}`, style: "display: block; float: left;", title: `${isEn ? "View" : "Просмотр"}` });
                    addElement("img", sectorViewReference, { style: "border: 0;", src: "data:image/gif,GIF89a%10%00%10%00%D5%00%00B%40B%15%15%18((-PPUzz%7FHHJ%5D%5D_--.zz%7BWWX23%3BEHS%80%88%A2rx%8C%DA%DD%E7bj%80sx%87%88%8C%97%7D%85%98HO_%7D%85%97%88%8C%95psz%DC%DD%DF%D4%D5%D7x%7F%8C%2B-0%C2%DF%FF%DF%F4%FFMOPmop%EF%F1%F2%E7%F9%FF%EA%FD%FF%E7%FF%FF%13%15%15%E9%FF%FF%18%1A%1A%FA%FF%FFmoo%FC%FF%FF%FD%FF%FFZ%5B%5B%5D%5D%5B%40%3F%3F%FF%FF%FF%16%16%16%10%10%10%05%05%05%00%00%00%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%002%00%2C%00%00%00%00%10%00%10%00%00%06%5C%40%99pH%2C%12%13%01%8D%80e%24%02ZP%A8%A2)k%C8%A2%D1As%01Ma'%CDF%D4%14%7D4!-%94%A8%95%BAJ%9A%07P%8B%E4mm%2C%D4H(%CA%A9%AC%A82%05%19%0C%14%041*%17%80E0%1D%18%8AD%2F%06%89%8FB%25'%1F%94B.%08-%992%23%1E%9ECA%00%3B" });
                    sectorViewReference.addEventListener("click", function(e) { e.stopPropagation(); })
                    
                    if([2, 6, 16, 21].includes(parseInt(locationNumber))) {
                        getMoveToMapObjectReference('/mercenary_guild.php', tableCell, locationNumber, "https://dcdn.heroeswm.ru/i/btns/job_fl_btn_mercenary.png", "display: block; float: right; width: 16px; height: 16px;");
                    }
                    const guestInfo = getGuestInfo(locationNumber);
                    const guestInfoKeys = Object.keys(guestInfo);
                    if(guestInfoKeys.length > 0) {
                        const guestInfoTitles = guestInfoKeys.map(x => `${guestInfo[x].HostInfo} до ${(new Date(guestInfo[x].ExpireDate)).toLocaleString()}`);
                        const guestReference = addElement("a", tableCell, { href: `/house_info.php?id=${guestInfoKeys[0]}`, style: "display: block; float: left;", title: guestInfoTitles.join("\n") });
                        guestReference.addEventListener("click", function(e) { e.stopPropagation(); })
                        addElement("img", guestReference, { style: "border: 0;", src: "https://dcdn.heroeswm.ru/i/btns/job_fl_btn_houses.png", style: "width: 16px; height: 16px;" });
                    }
                } else {
                    tableCell.style.color = loc[5].Color;
                    tableCell.addEventListener("click", function() { getURL(loc[5].Url); }, false);
                    if(x == 50 && y == 54) {
                        addElement("br", tableCell);
                        AmbushMoratoriumPanel = addElement("span", tableCell, { id: "AmbushMoratoriumPanel" });
                    }
                    if(x == 51 && y == 54) {
                        addElement("br", tableCell);
                        addElement("span", tableCell, { id: "SmithMoratoriumPanel" });
                    }
                }
            }
        }
    }
    processMercenaryTaskLocation();
    return mapMoving;
}
function drawAmbushMarks() {
    for(const locationNumber of Object.keys(locations).filter(x => parseInt(x) < 100)) {
        const routeSettings = routes[playerLocationNumber][locationNumber];
        const tableCell = document.getElementById(`transporterLocation${locationNumber}`);
        const thiefwarrioranip33 = tableCell.querySelector("img[src*='thiefwarrioranip33']");
        if(routeSettings && routeSettings[1] < 2 && !GM_getValue("AmbushSuspendExpireDate") && !GM_getValue("AmbushState") && isFullHealth()) {
            if(!thiefwarrioranip33) {
                const ambushImage = addElement("img", tableCell, { src: "https://dcdn.heroeswm.ru/i/portraits/thiefwarrioranip33.png", style: "width: 16px; height: 16px;", title: `${isEn ? "Ambush" : "Засада"}` });
                ambushImage.addEventListener("click", function(e) { e.stopPropagation(); setThiefAmbush(locationNumber); });
            }
        } else {
            if(thiefwarrioranip33) {
                thiefwarrioranip33.remove();
            }
        }
    }
}
async function processMercenaryTaskLocation() {
    const mercenaryTaskLocation = await getMercenaryTaskLocation();
    if(mercenaryTaskLocation) {
        addElement("img", document.getElementById(`transporterLocation${mercenaryTaskLocation}`), { src: "https://dcdn2.heroeswm.ru/i/cssmap/map_sectors_naim.png", style: "width: 16px; height: 16px;", title: `${isEn ? "Mercenary guild task" : "Задание гильдии наемников"}` });
    }
}
function getMercenaryTaskLocation() {
    return new Promise((resolve, reject) => {
        if(location.pathname == '/map.php') {
            setTimeout(function() {
                if(windowObject.naim_type == 'fight') {
                    GM_setValue("MercenaryTaskLocation", windowObject.naim_sector);
                } else {
                    GM_deleteValue("MercenaryTaskLocation");
                }
                resolve(GM_getValue("MercenaryTaskLocation"));
            }, 300);
        } else {
            resolve(GM_getValue("MercenaryTaskLocation"));
        }
    });
}
async function findNearestWork(factoryType) {
    const movingEnabled = await varifyMovingEnabled();
    let notEmptyLocationsSortedByDistance = [playerLocationNumber];
    if(movingEnabled) {
        notEmptyLocationsSortedByDistance = getLocationsSortedByDistance();
    }
    notEmptyLocationsSortedByDistance = notEmptyLocationsSortedByDistance.filter(x => !GM_getValue(`LastDetectedEmptyFactoriesDate_${factoryType}${x}`));
    const workFindingMessageSpan = document.querySelector("span#workFindingMessageSpan");
    for(const locationNumber of notEmptyLocationsSortedByDistance) {
        workFindingMessageSpan.innerText = `Viewing ${locationNumber}...`;
        var findedWorkObject = await findWorkInLocation(factoryType, locationNumber);
        if(findedWorkObject) {
            if(gmGetBool("MoveToWorkAfterFind")) {
                tryMoving(locationNumber, findedWorkObject.href);
            } else {
                getURL(findedWorkObject.href);
            }
            workFindingMessageSpan.innerText = "";
            break;
        }
    }
    if(!findedWorkObject) {
        workFindingMessageSpan.innerText = "Vacancy not found";
    }
}
async function findWorkInLocation(factoryType, locationNumber) {
    const x = locations[locationNumber][0];
    const y = locations[locationNumber][1];
    const doc = await getRequest(`map.php?cx=${x}&cy=${y}&st=${factoryType}`);
    const objectRefs = doc.querySelectorAll("a[href^='object-info.php?id=']");
    //console.log(`objectRefs: ${objectRefs.length}`);
    for(const objectRef of objectRefs) {
        //console.log(`objectRef.innerText: ${objectRef.innerText}, ${objectRef.innerText.includes("\u00bb\u00bb\u00bb")}`);
        if(objectRef.innerText.includes("\u00bb\u00bb\u00bb") && !objectRef.querySelector("font")) {
            const salary = parseInt(objectRef.parentNode.previousElementSibling.querySelector("b").innerHTML);
            const minSalary = parseInt(GM_getValue("MinSalary", 0));
            //console.log(`minSalary: ${minSalary}, salary: ${salary}, objectRef: ${objectRef}`);
            if(minSalary == 0 || salary >= minSalary) {
                return objectRef;
            }
        }
    }
}
function getRequest(url) {
    return new Promise((resolve, reject) => {
        GM.xmlHttpRequest({ method: "GET", url: url, overrideMimeType: "text/html; charset=windows-1251",
            onload: function(response) { resolve((new DOMParser).parseFromString(response.responseText, "text/html")); },
            onerror: function(error) { reject(error); }
        });
    });
}
function postRequest(url, data) {
    return new Promise((resolve, reject) => {
        GM.xmlHttpRequest({ method: "POST", url: url, headers: { "Content-Type": "application/x-www-form-urlencoded" }, data: data,
            onload: function(response) { resolve(response); },
            onerror: function(error) { reject(error); }
        });
    });
}
function getLocationsSortedByDistance() {
    if(playerLocationNumber in routes && routes[playerLocationNumber]) {
        const playerLocationRoutes = routes[playerLocationNumber];
        const availableLocationsDistance = { };
        availableLocationsDistance[playerLocationNumber] = 0;
        for(let i = 0; i < playerLocationRoutes.length; i++) {
            if(playerLocationRoutes[i]) {
                availableLocationsDistance[i] = playerLocationRoutes[i][1];
            }
        }
        const locationsSortedByDistance = Object.keys(availableLocationsDistance).sort(function(a,b) { return availableLocationsDistance[a] - availableLocationsDistance[b]; });
        //console.log(locationsSortedByDistance);
        return locationsSortedByDistance;
    }
}
function getLocationNumberByCoordinate(x, y) {
    for(let locationNumber in locations) {
        if(locations[locationNumber][0] == x && locations[locationNumber][1] == y) {
            return locationNumber;
        }
    }
}
function processMoving() {
    let targetLocationNumber = parseInt(GM_getValue("TargetLocationNumber"));
    //console.log(`playerLocationNumber: ${playerLocationNumber}, TargetLocationNumber: ${GM_getValue("TargetLocationNumber")}, LastMoveTryFrom: ${GM_getValue("LastMoveTryFrom")}`);
    if(GM_getValue("LastMoveTryFrom") == playerLocationNumber && targetLocationNumber != playerLocationNumber) {
        stopMoving(); // Мы не смогли уйти из локации, поэтому останавливаем движение; и мы не движемся на объект в этой же локации
        return;
    }
    let arrived = false;
    if(targetLocationNumber) {
        if(targetLocationNumber == playerLocationNumber) {
            GM_deleteValue("TargetLocationNumber");
            GM_deleteValue("LastMoveTryFrom");
            GM_deleteValue("LastMoveTryTo");
            arrived = true;
        } else {
            goToNextRoutePoint();
        }
    }
    //console.log([playerLocationNumber, targetLocationNumber, arrived, GM_getValue("EnterObjectOnArrived")]);
    // Перемещение к объекту после прибытия
    if(arrived) {
        let enterObjectOnArrived = GM_getValue("EnterObjectOnArrived");
        if(enterObjectOnArrived) {
            GM_deleteValue("EnterObjectOnArrived");
            getURL(enterObjectOnArrived);
            //setTimeout(function() { window.location = enterObjectOnArrived; }, 300);
        }
    }
}
function tryMoving(targetLocationNumber, enterObjectOnArrived) {
    if(!MountInfo.ComplexRoute) {
        alert(isEn ? "Script working only if mount with complex route enabled" : "Скрипт работает, если есть транспорт со сложным маршрутом");
        return;
    }
    if(targetLocationNumber == playerLocationNumber && !enterObjectOnArrived) {
        alert(YouAreHere);
        return;
    }
    if(targetLocationNumber == 25) {
        alert(How);
        return;
    }
    checkMovingEnabledAndGo(targetLocationNumber, enterObjectOnArrived);

}
async function varifyMovingEnabled() {
    let movingEnabled = true;
    if(/map.php/.test(location.href)) {
        const mapNavigator = document.querySelector("#map_navigator");
        movingEnabled = (Array.from(mapNavigator.querySelectorAll("div[id*='dbut']")).find(x => x.style.display == "block")) ? true : false;
    } else {
        const doc = await getRequest("/inventory.php");
        const invNote = doc.querySelector("div.inv_note_inside");
        movingEnabled = !(invNote && invNote.innerHTML.includes(YouAreInAChallenge));
    }
    return movingEnabled;
}
async function checkMovingEnabledAndGo(targetLocationNumber, enterObjectOnArrived) {
    let movingEnabled = true;
    if(targetLocationNumber != playerLocationNumber) {
        movingEnabled = await varifyMovingEnabled();
    }
    if(movingEnabled) {
        undressAndGo(targetLocationNumber, enterObjectOnArrived);
    } else {
        alert(YouAreInAChallenge);
    }
}
async function undressAndGo(targetLocationNumber, enterObjectOnArrived) {
    if(gmGetBool("removeArtsBeforeMoving") && targetLocationNumber != playerLocationNumber) {
        await getRequest("/inventory.php?all_off=100");
    }
    startMoving(targetLocationNumber, enterObjectOnArrived);
}
function startMoving(targetLocationNumber, enterObjectOnArrived) {
    GM_setValue("TargetLocationNumber", targetLocationNumber);
    if(enterObjectOnArrived) {
        GM_setValue("EnterObjectOnArrived", enterObjectOnArrived);
    }
    goToNextRoutePoint();
}
function goToNextRoutePoint() {
    const targetLocationNumber = parseInt(GM_getValue("TargetLocationNumber"));
    let nextRoutePoint;
    if(MountInfo.ComplexRoute && !gmGetBool("IgnoreComplexRoute")) {
        nextRoutePoint = targetLocationNumber;
    } else if(playerLocationNumber in routes && targetLocationNumber in routes[playerLocationNumber] && routes[playerLocationNumber][targetLocationNumber]) {
        const route = routes[playerLocationNumber][targetLocationNumber][0];
        nextRoutePoint = route.split('-')[1];
    }
    if(nextRoutePoint) {
        GM_setValue("LastMoveTryFrom", playerLocationNumber);
        GM_setValue("LastMoveTryTo", nextRoutePoint);
        //getURL(`/move_sector.php?id=${nextRoutePoint}&rand=${Math.random()}`);
        if(playerLocationNumber != nextRoutePoint) {
            getURL(`/move_sector.php?id=${nextRoutePoint}&rand=${Math.random()}`);
        } else if(GM_getValue("EnterObjectOnArrived")) {
            processMoving();
        }
    }
}
function stopMoving() {
    GM_deleteValue("TargetLocationNumber");
    GM_deleteValue("EnterObjectOnArrived");
    GM_deleteValue("LastMoveTryFrom");
    GM_deleteValue("LastMoveTryTo");
}
function getPlayerLocationNumber() {
    if(location.pathname == '/map.php' && location.search == '') {
        // Если мы на карте без параметров, т.е. на локации, где сами находимся. Если мы не в пути, тогда видим предприятия. Мы можем обновить текущее положение игрока.
        const minesRef = document.querySelector("a[href*='map.php?cx='][href*='&cy='][href*='&st=mn']"); // Берем из ссылки на заголовке шахт данной локации
        if(minesRef) {
            const locationNumber = getLocationNumberFromMapUrlByXy(minesRef.href);
            if(locationNumber) {
                GM_setValue("PlayerLocationNumber", locationNumber);
                return locationNumber;
            }
        }
    }
    return parseInt(GM_getValue("PlayerLocationNumber")); // Иначе, возьмем из кеша. Там будет пусто только при первом запуске скрипта, когда мы не просматриваем карту.
}
function getLocationNumberFromMapUrlByXy(href) {
    const x = getUrlParamValue(href, "cx");
    const y = getUrlParamValue(href, "cy");
    for(let locationNumber in locations) {
        if(x == locations[locationNumber][0] && y == locations[locationNumber][1]) {
            return locationNumber;
        }
    }
}
function getMoveToObjectReference(objectUrl, parentNode) {
    const objectId = getUrlParamValue(objectUrl, "id");
    const objectLocationNumber = objectLocations[objectId];
    //console.log(`objectId: ${objectId}, objectLocationNumber: ${objectLocationNumber}, parentNode: ${parentNode}`);
    return getMoveToMapObjectReference(objectUrl, parentNode, objectLocationNumber);
}
function getMoveToMapObjectReference(objectUrl, parentNode, objectLocationNumber, image, imageStyle) {
    //console.log(`objectLocationNumber: ${objectLocationNumber}, playerLocationNumber: ${playerLocationNumber}, image: ${image}`);
    if(objectLocationNumber != playerLocationNumber || image) {
        let routeTime = "";
        const routeSettings = routes[playerLocationNumber][objectLocationNumber];
        if(routeSettings) {
            const routeLength = routeSettings[1];
            routeTime = `\n${TravelingTimeName} ${routeLength * MountInfo.TravelingTime} ${Second}`;
        }
        const moveHereReference = createElement('span', { style: "cursor: pointer;", innerHTML: `<img src="${image || getHorseImageData()}" alt="${MoveHere}" title="${MoveHere + routeTime}" align="absmiddle" height="15" width="15" style="max-height: 12px; max-width: 12px;${imageStyle || ""}">&nbsp;&nbsp;` });
        moveHereReference.onclick = function(e) { tryMoving(objectLocationNumber, objectUrl); e.stopPropagation(); };
        //console.log(`objectUrl: ${objectUrl}, moveHereReference: ${moveHereReference}, routeTime: ${routeTime}, parentNode: ${parentNode}, parentNode.nextSibling: ${parentNode.nextSibling}, parentNode.nextElementSibling: ${parentNode.nextElementSibling}, parentNode.parentNode: ${parentNode.parentNode}, parentNode.previousSibling: ${parentNode.previousSibling}`);
        if(image) {
            parentNode.appendChild(moveHereReference);
        } else {
            const insertResult = parentNode.parentNode.insertBefore(moveHereReference, parentNode.nextSibling);
            //console.log(`insertResult: ${insertResult}, parentNode: ${parentNode}`);
        }
        return moveHereReference;
    }
}
async function setThiefAmbush(locationNumber) {
    if(checkSectorForAmbush(locationNumber)) {
        if(GM_getValue("AmbushSuspendExpireDate")) {
            alert(`${ItIsTooEarly}`);
        } else {
            await postRequest("/thief_ambush.php", `id=${locationNumber}&with_who=0`);
            getURL("/map.php");
        }
    } else {
        alert(AmbushMaySettedOnAdjacentSector);
    }
}
function checkSectorForAmbush(locationNumber) { 
    if(playerLocationNumber in routes && locationNumber in routes[playerLocationNumber] && routes[playerLocationNumber][locationNumber]) {
        const route = routes[playerLocationNumber][locationNumber][0];
        const nextRoutePoint = route.split('-')[1];
        return nextRoutePoint == locationNumber;
    }
    return false;
}
function resetMountInfo() {
    GM_deleteValue("TravelingTime");
    GM_deleteValue("ComplexRoute");
    GM_deleteValue("EndDate");
    GM_deleteValue("Antithief");
    GM_deleteValue("AntithiefControlled");
    GM_deleteValue("ToggleAntithiefKey");
    GM_deleteValue("IsPremiunAccount");
}
async function getMountInfo(force) {
    let endDate = GM_getValue("EndDate") ? parseInt(GM_getValue("EndDate")) : undefined;
    if(!endDate || endDate <= Date.now() || force) {
        if(endDate <= Date.now()) {
            resetMountInfo();
        }
        const doc = await getRequest("/shop.php?cat=transport");
        const mounts = doc.querySelectorAll('div.s_art_r');
        //console.log(["mounts", mounts.length]);
        const mountsInfoGeted = mounts.length > 0; // Если 0, то мы в пути и на страницу магазина нас не пускают. Поэтому используем старые данные о транспорте
        if(mountsInfoGeted) {
            let isPremiunAccount = false;
            for(const mount of mounts) {
                if(mount.innerHTML.includes(YourPremiumMountUntil) || mount.innerHTML.includes(YourMountUntil)) {
                    var mountRow = mount;
                    isPremiunAccount = mount.innerHTML.includes(YourPremiumMountUntil);
                    break;
                }
            }
            if(mountRow) {
                resetMountInfo();
                GM_setValue("IsPremiunAccount", isPremiunAccount);
                const mountExpireTimeDiv = mountRow.querySelector('div.s_art_note_transport') || mountRow.querySelector('div.s_art_name.s_transport_name');
                const dateString = (mountExpireTimeDiv.querySelector('span') || mountExpireTimeDiv).innerText.split(Until)[1];
                GM_setValue("EndDate", parseDate(dateString).getTime());
                const transportOptions = mountRow.querySelectorAll("div.s_art_prop_transport");
                for(let transportOption of transportOptions) {
                    if(transportOption.innerHTML.includes(TravelingTimeName)) {
                        const timeValue = /(\d{1,2})/.exec(transportOption.innerHTML);
                        GM_setValue("TravelingTime", timeValue[1]);
                    }
                    if(transportOption.innerHTML.includes(ComplexRouteName)) {
                        GM_setValue("ComplexRoute", transportOption.querySelector("img.s_art_prop_transport_amount_image[src*='check_yes']") ? true : false);
                    }
                    if(transportOption.innerHTML.includes(Antithief)) {
                        let antithiefControlImage = transportOption.querySelector("img.s_art_prop_transport_amount_image[src*='check_on']");
                        antithiefControlImage = antithiefControlImage || transportOption.querySelector("img.s_art_prop_transport_amount_image[src*='check_off']");
                        GM_setValue("Antithief", antithiefControlImage ? true : false);
                        GM_setValue("AntithiefControlled", antithiefControlImage ? true : false);
                        if(antithiefControlImage) {
                            GM_setValue("ToggleAntithiefKey", getUrlParamValue(antithiefControlImage.parentNode.href, "t_on") || getUrlParamValue(antithiefControlImage.parentNode.href, "t_off"));
                        }
                    }
                }
            }
        }
    }
    const mountInfo = {
        TravelingTime: parseInt(GM_getValue("TravelingTime", DefaultTravelingTime)),
        ComplexRoute: gmGetBool("ComplexRoute"),
        EndDate: GM_getValue("EndDate") ? parseInt(GM_getValue("EndDate")) : undefined,
        Antithief: gmGetBool("Antithief"),
        AntithiefControlled: gmGetBool("AntithiefControlled"),
        IsPremiunAccount: gmGetBool("IsPremiunAccount")
    };
    //console.log(mountInfo);
    return mountInfo;
}
function processHouseInfo() {
    if(location.pathname == '/house_info.php') {
        const houseId = getUrlParamValue(location.href, "id");
        const hostDiv = document.querySelector("div#tt");
        const container = getParent(hostDiv, "table");
        const loc = container.querySelector("a[href^='map.php?cx=']");
        const locationNumber = getLocationNumberFromMapUrlByXy(loc.href);
        const ownerName = container.querySelector("a[href^='pl_info.php?id=']").querySelector("b").innerText;
        const hostName = hostDiv.querySelector("b").innerText;
        let maxExpireDate;
        const guestRoomDivs = container.querySelectorAll("div[id^='gr']");
        for(const guestRoomDiv of guestRoomDivs) {
            const playerReference = guestRoomDiv.querySelector(`a[href='pl_info.php?id=${PlayerId}']`)
            if(playerReference && playerReference.nextSibling) {
                const re = /(\d{2}:\d{2} \d{2}-\d{2})/.exec(playerReference.nextSibling.nodeValue);
                if(re) {
                    const expireDate = parseDate(re[1], true);
                    if(!maxExpireDate || maxExpireDate < expireDate) {
                        maxExpireDate = expireDate;
                    }
                }
            }
        }
        const guestInfo = getGuestInfo(locationNumber);
        if(maxExpireDate) {
            guestInfo[houseId] = { HostInfo: `${ownerName} (${hostName})`, ExpireDate: maxExpireDate.toJSON() };
        }
        GM_setValue(`GuestInfo${locationNumber}`, JSON.stringify(guestInfo));
        //console.log(guestInfo);
    }
}
function getGuestInfo(locationNumber) {
    if(GM_getValue(`GuestInfo${locationNumber}`)) {
        const now = new Date();
        const guestInfo = JSON.parse(GM_getValue(`GuestInfo${locationNumber}`));
        for(const guestInfoKey of Object.keys(guestInfo)) {
            if(new Date(guestInfo[guestInfoKey].ExpireDate) < now) {
                var repackNeeded = true;
                delete guestInfo[guestInfoKey];
            }
        }
        const newKeys = Object.keys(guestInfo);
        if(repackNeeded) {
            if(newKeys.length == 0) {
                GM_deleteValue(`GuestInfo${locationNumber}`);
            } else {
                GM_setValue(`GuestInfo${locationNumber}`, JSON.stringify(guestInfo));
            }
        }
        if(newKeys.length > 0) {
            return guestInfo;
        }
    }
    return {};
}

function parseDate(dateString, isFuture = false) {
    //console.log(dateString)
    if(!dateString) {
        return;
    }
    const dateStrings = dateString.split(" ");
    
    let hours = 0;
    let minutes = 0;
    let seconds = 0;
    const timePart = dateStrings.find(x => x.includes(":"));
    if(timePart) {
        const time = timePart.split(":");
        hours = parseInt(time[0]);
        minutes = parseInt(time[1]);
        if(time.length > 2) {
            seconds = parseInt(time[2]);
        }
    }

    const now = new Date();
    let year = now.getFullYear();
    let month = now.getMonth();
    let day = now.getDate();
    const datePart = dateStrings.find(x => x.includes("-"));
    if(datePart) {
        const date = datePart.split("-");
        month = parseInt(date[isEn ? (date.length == 3 ? 1 : 0) : 1]) - 1;
        day = parseInt(date[isEn ? (date.length == 3 ? 2 : 1) : 0]);
        if(date.length == 3) {
            year = isEn ? parseInt(date[0]) : parseInt(date[2]);
            if(year < 1000) {
                year += Math.floor((new Date()).getFullYear() / 1000) * 1000;
            }
        } else {
            if(isFuture && month == 0 && now.getMonth() == 11) {
                year += 1;
            }
        }
        
    }
    //console.log(`year: ${year}, month: ${month}, day: ${day}, time[0]: ${time[0]}, time[1]: ${time[1]}, ${new Date(year, month, day, parseInt(time[0]), parseInt(time[1]))}`);
    return new Date(year, month, day, hours, minutes, seconds);
}
function addElement(type, parent, data, insertFirst = false) {
    let el = createElement(type, data);
    if(parent) {
        if(insertFirst) {
            parent.insertBefore(el, parent.firstChild);
        } else {
            parent.appendChild(el);
        }
    }
    return el;
}
function createElement(type, data) {
    let el = document.createElement(type);
    if(data) {
        for(let key in data) {
            if(key == "innerText" || key == "innerHTML") {
                el[key] = data[key];
            } else {
                el.setAttribute(key, data[key]);
            }
        }
    }
    return el;
}
function getHorseImageData() { return "data:image/png;base64,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"; }
function getUrlParamValue(url, paramName) { return (new URLSearchParams(url.split("?")[1])).get(paramName); }
function getURL(url) { window.location.href = url; }
function gmGetBool(valueName) {
    const value = GM_getValue(valueName);
    if(value) {
        if(typeof(value) == "string") {
            return value == "true";
        }
        if(typeof(value) == "boolean") {
            return value;
        }
    }
    return false;
}
async function loadFactories() {
    let selectedJobDiv = document.querySelector("div.job_fl_btn.show_hint.job_fl_btn_selected");
    if(!selectedJobDiv) {
        return; // Мы в пути
    }
    let currentFactoryType = getUrlParamValue(selectedJobDiv.parentNode.href, "st");
    const viewingLocationNumber = getLocationNumberFromMapUrlByXy(selectedJobDiv.parentNode.href);
    let indexOfCurrentFactoryType = factoryTypes.indexOf(currentFactoryType);
    if(indexOfCurrentFactoryType == -1) {
        return;
    }
    const mapRightBlock = document.getElementById("map_right_block");
    //const mapRightBlockInside = document.getElementById("map_right_block_inside");
    //console.log(`${mapRightBlock.id}, offsetHeight: ${mapRightBlock.offsetHeight}, height: ${mapRightBlock.style.height}`);
    //console.log(`${mapRightBlockInside.id}, offsetHeight: ${mapRightBlockInside.offsetHeight}, height: ${mapRightBlockInside.style.height}`);
    let mapRightBlockHeight = mapRightBlock.offsetHeight;
    let mainFactoriesTable = mapRightBlock.querySelector("table.wb");
    if(!mainFactoriesTable) {
        return;
    }
    let mapRef = document.querySelector("a[href^='map.php?cx=']");
    let x = getUrlParamValue(mapRef.href, "cx");
    let y = getUrlParamValue(mapRef.href, "cy");
    let factoriesTableContainer = mainFactoriesTable.parentNode;
    const factoryTables = { };
    factoryTables[currentFactoryType] = mainFactoriesTable;
    for(const factoryType of factoryTypes) {
        if(factoryType != currentFactoryType && gmGetBool("Load" + factoryType) && GM_getValue(`LastDetectedEmptyFactoriesDate_${factoryType}${viewingLocationNumber}`, 0) + 1000 * 60 * 60 * 24 * 30 < Date.now()) {
            const doc = await getRequest(`map.php?cx=${x}&cy=${y}&st=${factoryType}`);
            const docMapRightBlock = doc.getElementById("map_right_block");
            factoryTables[factoryType] = docMapRightBlock.querySelector("table.wb");
        }
    }
    for(let i = 0; i < factoryTypes.length; i++) {
        let tableElement = factoryTables[factoryTypes[i]];
        if(!tableElement) {
            continue;
        }
        const isTableEmpty = tableElement.rows.length <= 2 && (tableElement.rows.length < 2 || tableElement.rows[1].cells.length == 0);
        if(isTableEmpty) {
            GM_setValue(`LastDetectedEmptyFactoriesDate_${factoryTypes[i]}${viewingLocationNumber}`, Date.now());
        } else if(GM_getValue(`LastDetectedEmptyFactoriesDate_${factoryTypes[i]}${viewingLocationNumber}`)) {
            GM_deleteValue(`LastDetectedEmptyFactoriesDate_${factoryTypes[i]}${viewingLocationNumber}`);
        }
        if(!isTableEmpty) {
            if(indexOfCurrentFactoryType < i) {
                factoriesTableContainer.appendChild(tableElement)
                mapRightBlockHeight += tableElement.offsetHeight;
            }
            if(indexOfCurrentFactoryType > i) {
                factoriesTableContainer.insertBefore(tableElement, mainFactoriesTable);
                mapRightBlockHeight += tableElement.offsetHeight;
            }
        } else if(indexOfCurrentFactoryType == i) {
            mapRightBlockHeight -= mainFactoriesTable.offsetHeight;
            factoriesTableContainer.removeChild(mainFactoriesTable); // Если текущий список предприятий - пустой, то уберем его
        }
    }
    mapRightBlock.style.height = `${mapRightBlockHeight}px`;
}
function ClientHeight() { return document.compatMode == 'CSS1Compat' && document.documentElement ? document.documentElement.clientHeight : document.body.clientHeight; }
function ShowBigData(data) { addElement("TEXTAREA", document.body, { innerHTML: data }); }
function getParent(element, parentType, number = 1) {
    let result = element;
    let foundNumber = 0;
    while(result = result.parentNode) {
        if(result.nodeName.toLowerCase() == parentType.toLowerCase()) {
            foundNumber++;
            if(foundNumber == number) {
                return result;
            }
        }
    }
}
function observe(target, handler, config = { childList: true, subtree: true }) {
    const ob = new MutationObserver(async function(mut, observer) {
        //console.log(`Mutation start`);
        observer.disconnect();
        if(handler.constructor.name === 'AsyncFunction') {
            await handler();
        } else {
            handler();
        }
        observer.observe(target, config);
    });
    ob.observe(target, config);
}
async function initUserName() {
    if(!GM_getValue("TransporterUserName")) {
        const doc = await getRequest(`/pl_info.php?id=${PlayerId}`);    
        GM_setValue("TransporterUserName", doc.querySelector("h1").innerText);
    }
}