hwmTimers

Таймеры гильдии рабочих, воров, наёмников, рейнджеров, охотников, кузнецов, восстановления здоровья и маны

目前為 2023-12-16 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name           hwmTimers
// @namespace      Tamozhnya1
// @author         Tamozhnya1
// @description    Таймеры гильдии рабочих, воров, наёмников, рейнджеров, охотников, кузнецов, восстановления здоровья и маны
// @version        7.0
// @include        https://www.heroeswm.ru/*
// @include        https://www.lordswm.com/*
// @exclude        */rightcol.php*
// @exclude        */ch_box.php*
// @exclude        */chat*
// @exclude        */ticker.html*
// @exclude        */frames*
// @exclude        */brd.php*
// @grant          GM_deleteValue
// @grant          GM_getValue
// @grant          GM_setValue
// @grant 		   GM.notification
// @license        MIT
// ==/UserScript==

if(!this.GM_getValue || (this.GM_getValue.toString && this.GM_getValue.toString().indexOf("not supported") > -1)) {
    this.GM_getValue = function(key, def) { return localStorage[key] || def; };
    this.GM_setValue = function(key, value) { localStorage[key] = value; };
    this.GM_deleteValue = function(key) { return delete localStorage[key]; };
}
const playerIdMatch = document.cookie.match(/pl_id=(\d+)/);
if(!playerIdMatch) {
    return;
}
const PlayerId = playerIdMatch[1];
const isEn = document.documentElement.lang == "en";
const isHeartOnPage = document.querySelector("canvas#heart") || document.querySelector("div#heart_js_mobile") ? true : false;
const isNewInterface = document.querySelector("div#hwm_header") ? true : false;
const mooving = location.pathname == '/map.php' && !document.getElementById("map_right_block");
const windowObject = window.wrappedJSObject || unsafeWindow;
let tickTimer;
const ChallengeState = { Thrown: 1, Battle: 2 };
// в бою
if(location.pathname == '/war.php') {
    onWarDetected();
    return;
}
if(!isHeartOnPage) {
    return;
}
const [army_percent] = healthTimer();
var disable_alarm_delay = 30; //секунд задержки после предыдущего сигнала
if(!GM_getValue(`hwmTimersOptions${PlayerId}`)) {
    GM_setValue(`hwmTimersOptions${PlayerId}`, JSON.stringify({
    "time_health_alert": "no",
    "time_work_alert": "yes",
    "time_work_trudogolik": "0",
    "time_sm_alert": "yes",
    "time_gn_alert": "yes",
    "time_go_alert": "yes",
    "map_hunter": "false",
    "time_gv_alert": "yes",
    "map_thief_ambush": "false",
    "time_percent_faster": "1",
    "time_percent_prem": "1",
    "time_percent_prem_exp": "0",
    "time_percent_prem_title": "",
    "time_percent_lic_mo": "1",
    "time_percent_lic_mo_exp": "0",
    "time_percent_lic_mo_title": "",
    "gv_or_gre": "0",
    "gre_check": "0",
    "time_work_trudogolik_show": "1",
    "time_work_trudogolik_off": "0",
    "isShowWorkTimer": "1",
    "isShowSmithTimer": "1",
    "isShowMercTimer": "1",
    "isShowHuntTimer": "1",
    "isShowThiefTimer": "1",
    "disable_multiple_alarms": "1",
    "audio_file_gr": "",
    "audio_file_gonv": "",
    "audio_file_gk": "",
    "audio_file_h": "",
    "gl_attack": "false"
    }));    
}

let options = JSON.parse(GM_getValue(`hwmTimersOptions${PlayerId}`));;
var audio_default;
var audio_gr;
var audio_gonv;
var audio_gk;
var audio_h;
initAudios();

var texts = setTexts();
main();
function main() {
    initUserName();
    if(location.pathname == '/object_do.php' || location.href.indexOf('/object-info.php') > -1) {
        if(document.body.innerHTML.match(texts.work_obj_do)) {
            GM_setValue(`WorkEnd${PlayerId}`, Date.now() + 60 * 60000);
            GM_setValue(`LastWorkObjectId${PlayerId}`, getUrlParamValue(location.href, "id"));
            options.time_work_trudogolik = '' + (Number(options.time_work_trudogolik) + 1);
        }
    }
    if(location.pathname == '/home.php') {
        const workPlace = Array.from(document.querySelectorAll("a[href^='object-info.php']")).find(x => getParent(x, "td").innerHTML.includes(isEn ? "Currently employed at:" : "Место работы:"))
        if(workPlace) {
            GM_setValue(`LastWorkObjectId${PlayerId}`, getUrlParamValue(workPlace.href, "id"));
        }
    }
    createTimersPanel();
    requestServerTime();
    loadWorkEndTime();
    checkPremiumTime();
    checkLicMoO();
    checkWar();
    checkWork();
    checkMercenary();
    if(location.pathname == "/leader_guild.php") {
        Array.from(document.querySelectorAll("form[name^='f'] input[type='submit']")).forEach(x => x.addEventListener("click", function () { updateOption("gl_attack", 'true'); }));
    }
    checkRangerGuild();
    checkModWorkebench();
    if(location.pathname == '/map.php') {
        checkMapThief();
        checkMapRanger();
        checkMapHunter();
    }
    bindTimersPanel();
    launchTimers();
    GM_setValue(`hwmTimersOptions${PlayerId}`, JSON.stringify(options));
    console.log(`AmbushTimeout: ${GM_getValue(`AmbushTimeout${PlayerId}`)}, map_thief_ambush: ${options.map_thief_ambush}, map_hunter: ${options.map_hunter}, army_percent: ${army_percent}, HoldBattle: ${GM_getValue(`HoldBattle${PlayerId}`)}`);
}
function onWarDetected() {
    if(/warlog\|0/.exec(document.querySelector("html").innerHTML)) {
        //flash & html: warlog|0| -> бой происходит сейчас, warlog|1| -> запись боя, |player|7146446| -> id текущего игрока
        const playerIdExec = /\|player\|(\d+)\|/.exec(document.querySelector("html").innerHTML);
        if(playerIdExec && playerIdExec[1] == PlayerId) {
            GM_setValue(`HoldBattle${PlayerId}`, true);
        }
    }
    if(location.pathname == '/war.php') {
        const warId = getUrlParamValue(location.href, "warid");
        const lt = getUrlParamValue(location.href, "lt");
        const finalResultDiv = document.getElementById("finalresult_text");
        if(lt != "-1" && finalResultDiv.innerHTML.length <= 10 && GM_getValue("ChallengeState") == ChallengeState.Thrown) {
            GM_deleteValue("AmbushBeginDate");
            GM_setValue("ChallengeState", ChallengeState.Battle);
            observe(finalResultDiv, parseBattleResultPanel);
        }
    }
}
function parseBattleResultPanel() {
    const finalResultDiv = document.getElementById("finalresult_text");
    if(GM_getValue("ChallengeState") == ChallengeState.Battle && finalResultDiv.innerHTML.length > 10) {
        GM_deleteValue("ChallengeState");
        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}, UserName: ${GM_getValue("UserName")}`);
                if(bold.parentNode.nextSibling.nextSibling.firstChild.innerText == GM_getValue("UserName")) {
                    result = "win";
                }
                break;
            }
        }
        if(result == "fail") {
            GM_setValue("AmbushSuspendExpireDate", Date.now() + AmbushMinutesInterval * 60 * 1000);
        }
        //console.log(`result: ${result}, AmbushSuspendExpireDate: ${GM_getValue("AmbushSuspendExpireDate")}`);
    }
}
function updateOption(key, val) {
    options = JSON.parse(GM_getValue(`hwmTimersOptions${PlayerId}`));
    options[key] = val;
    GM_setValue(`hwmTimersOptions${PlayerId}`, JSON.stringify(options));
}
function initAudios() {
    audio_default = new Audio(
        'data:audio/mp3;base64,' +
        '/+OAxAAAAAAAAAAAAEluZm8AAAAPAAAABQAACcoAMzMzMzMzMzMzMzMzMzMzMzMzM2ZmZmZmZmZmZmZmZmZmZmZmZmZmmZmZmZmZmZmZmZmZmZmZmZmZmZnMzMzMzMzMzMzMzMzMzMzMzMzMzP//////////////////////////AAAAOUxBTUUzLjk4cgE3AAAAAAAAAAAUQCQCTiIAAEAAAAnKGRQoyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+OAxABX1F4koVjAAQjCEwjMYQMNFN52kFuDMg7zN6S6b5yx01M0x11uJOV6eYZQoAsSQOw1xyGGKAISEUHFBIzpQ4IL0Piieg+pvC30VO49A5D8R9h6gag6YDTJKsOmOseB4gzhU6Y6g7E37tSik5MMoRMLYFtEUGmQaoGWkLwLonYbcty2tqnQCJEMsnK9Pbr07+P5GKTGvK4bf9yH8lmFSURixK2cM4dyWXYbcty5fz5h2GGKAKCOo4ZdxBxTRrjkNYdyUqZlkCzCKDEIpjTxt/3IchyIcxlb/v/D8s5Xp+yh/IcvUljCpKHYdyWXXYYYoAhIQCIOKaQlc5aQuIuicz1SVXbVIoAqRYjEIcvSt/3IfycrxuNy+/Uht/3/l9yGGsM4a4xN36ZlCJhchAAzRgYAAYAFmEHFiPxVhty2uO5OV43G7esK8bdt34vchhrC7FB2XypciEhAImA8jSy7iABdEUrxuNy/sdUgMCEYDAYDIYDEYDEYCeapz8c1sF1vw7MA2FaUsp8YwZDBEYNhWZMjfLOfb9r5p0lxsxHh/+OCxDRkHGbSX5nqA8jG0fWPNSWWZzFJWn3iM+keNcjuMPAiMRxVMchckCm7dWzuJA7X25tHSGDhiM0jUGg8MwA6MVRlMeRFc4LgGgPTMdOTpuI4vqhQgA4YtFoZTF4aHDYY3j+FQNMJQfAAi4ax7z82SvnelEritIYJgGucGiKYohiYMAUZKC4YkCcTDAZFD134cs58m6fmMst8qcprjzGKwLgIXzAkATBsETDcUzIgPDDwQjDgFTBwCQoEZgqBJhEF2OfcOc/Cn7hqWxqSTk3STW5fP0XgIdDBABjGAMDCQJAKCYQHaYBheG5YCoqAOYMA6BBHIQqMFgDblLOU/cKev2nzz53fK+7dLQasZSvPlXDehUIQ4CCIADBYCGJGCgFGBAZEIEgEFjB0AAQIqQxg4Dq9QgHUrTBsATAsGRCBBhuD///8z1+ff/P////+7/Llzes8/u/++cw59bO7zD///33////V4SolSuRCmPL6nAqR9ZQ0l+oyuAOCyR+7FK1oVDzAwRqbMUv6FhjNAQEMzLsmIgS1UOBgAImIjQoipf/jgsQ4ZLx2nAGY2ADAELIgUAh5h5eawLGRKBiAwYwIDS0ZmSGrrBAOmEGpgouigCBMxr2N3HiEvAQ2aIDGiCZphKHYxkpACC0WAgEWEJSRLJMWI9mGDJdQCFQcamMhKN7LzABcSEjHA8AA5elmCe6Wyi8XQAKiAgOjA3eGCAEC4GQCqXiwTLlTL5YaBQRe6EhFFL9fUOA0AL3Q64KYyQbgt8mkv1/V9P4JChgQoIASD1WhYEVpesZBH3VMAQBB5E1E5H4mAwAALneJRhsr1OGyC1DFaKvMma+jkOosKrLL2iNOadDTcpLBz1Mob+FzywCx3ThmMsqTXZiplIWdwPIXRj7WqRrDayxfVC8V+dl1aRR2NtKd6IwPBzgOHArgzcOydwWEtee5yqC3EKOBYBlNSIajDvVs696AXWjEzYj0zWiESlUWuWsb+M/aiFL3KW5SCe1fmpBWfr/+kk8SlmOX/9LVpKtJMzwBIiRGAngdQbAVvPs+70vm3FR5QGDWsugz5YIx02MFFn6TrWuxJy7xiJkYuqGZsQKPgUHGFgQUFQr/44LEOmbMdnABmdgAhKn3zEAY9pkI2cEtkxQaUamfHRgpKYkJoiCQyjYDgEw0JAwcu8oDzYms8RzMdCgUxpsEIUDSIEgZkBmBQsQASPSb7jusKggBA0bDEgFDEtgRBDSAMhKao2qGq3ofo0Q+pg+rdk92msTdeNCMGMCHzCgMUDQsEjAAFi9CS2NSLCkt1uQ+mMtOEyt6EAa4FRqWr4f2BncV48oMCRoXTaC4EoEAjss8vQVBCAAXmuFmzrq+bm26eLBIQ6LLnpruZTO0uh2WRZOBAEFID2syWVM6CoEIwRJp2m7JdqmZul6sLATxwJC3QdtNVrssgZuLSoKguZbDIG4uk+9O3sATbiWKSBtOUpasEyNjmC5Y7EZUwaJtNhpXUudGaf9nNO1xsbcFG2MwFLnphyLy+JPO0xpD708akEpls9Ny2Q0sbi71zrcYzFYlGnqxr7d+UvHK4nDjnf/wbDz0WJFR//tfoLTcY9mqAQMgAqUQjTA76PNjioDGCBQNciLWVhUVUiVilrWC2KV2VhS/qgKJpbEABLIqbRNHks6W/+OCxDNgzGY2JdjIAGUFX4R6LZMFZkWeMMQxQEuhEGaDZvQmaMmtfyoVKQASaC5sNmWQXSdaOsNSFQkoqoml/i0xbZMKFOCoCiqiqu53n+f6HpdnKX1ZUmMisptAzopelsS0qRSxlhkTi7xeJMJl0ndJL4u8g8sZymHJDISmWyhcyQyAZB5IpUzEp19lhlAlTLuZ0153qjKlAkVkVkVi7xaYtMumDFAkJSAZIpQYvEhKL+lpS2qKLTXiQTAEIwxEHXVw7KXZWFTFRVXc/Wcpf1/X9f2WzLWWIuLOPssMoEkMkKgFQCpgurAScxd4uUsWQ/Vdlhq7VSrti0y/stwymXBZzFoi1lhrEWuw7hammtM6fqbXKXVAAJhBoPOtnS8rQ1DUPWa0af52nKcpynKayu1drOX5vSp/n+hmMwy/rWVMS4JgCmIKuqVspXau1iLOXJcmNS7tWlpcJVGqXlVMQU1FMy45OC40VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ=='
    );
    audio_default.preload = 'auto';
    audio_gr = initAudio(options.audio_file_gr);
    audio_gonv = initAudio(options.audio_file_gonv);
    audio_gk = initAudio(options.audio_file_gk);
    audio_h = initAudio(options.audio_file_h);
}
function initAudio(src) {
    if(src && src !== '') {
        var audio = new Audio();
        audio.src = src;
        audio.preload = 'auto';
        return audio;
    } else {
        return audio_default;
    }
}
function setTexts() {
    var obj;
    if(isEn) {
        obj = {
            health_alert_ty: 'Army restore alarm on',
            health_alert_tn: 'Alarm once at army restore',
            work_alert_ty: 'Workshift alarm on',
            work_alert_tn: 'Alarm off',
            sm_alert_ty: 'Blacksmith alarm on',
            gn_alert_ty: 'Mercenaries Guild alarm on',
            regexp_timegn0: /Come back in (\d+) minutes\./,
            regexp_timegn1: /\. Time left: (\d+) minutes\./,
            regexp_timegn2: /ou have (\d+) minutes left/,
            regexp_timegn3: /\. Time left: (\d+) minutes\./,
            regexp_timegn4: /still have (\d+) minutes/,
            regexp_timegn5: /you still have \d+ attempts and (\d+) minutes/,
            regexp_gn_rep: /Reputation: <b>([\d\.]+)/,
            go_alert_ty: 'Hunters Guild alarm on',
            regexp_go_timer: 'Next hunt available in',
            gv_alert_ty: 'Thieves Guild alarm on',
            gre_alert_ty: 'Rangers Guild alarm on',
            regexp_timegre: /Come in (\d+) min/,
            time_home: /You may enroll again in (\d+) min/,
            time_home2: / since (\d{1,2}):(\d{1,2})/,
            workPlace: 'Work place:',
            alert_health: 'Troops ready: 100%',
            alert_work: 'LG: You may enroll again',
            alert_sm: 'BS: Blacksmith works are finished',
            alert_gn: 'MG: Mercenaries Guild has a quest for you',
            alert_go: 'HG: You notice traces ...',
            alert_gv: 'TG: You may set an ambush',
            alert_gre: 'RG: Rangers Guild has a quest for you',
            audio_file: 'Audio file ',
            alarm_mode: '<b>Timer alarm mode</b>:',
            alarm_mode_sound: 'audio',
            alarm_mode_alert: 'message',
            alarm_mode_both: 'notification',
            alarm_mode_none: 'both',
            h_t: 'health',
            gonv_t: 'MHT(R)G',
            gr_t: 'LG',
            gk_t: 'BS',
            gk_title: 'To Blacksmith',
            gn_t: 'MG',
            gn_title: 'To Mercenaries\' Guild',
            go_t: 'HG',
            go_title: 'To Hunters\' Guild',
            gv_t: 'TG',
            gv_title: 'To Thieves\' Guild',
            gre_t: 'RG',
            gre_title: 'To Rangers Guild post',
            mana_title: 'Settings',
            work_obj_do: 'You have successfully enrolled',
            work_unemployed: 'You are currently unemployed',
            regexp_map_go: 'During the journey you have access to the',
            go_title_lic: 'The license expires ',
            alert_go_lic_exp: 'HG: Hunter license has expired',
            alert_prem_exp: 'Abu-Bakir\'s Charm has expired',
            st_start: 'All settings adjustments will apply after page is reloaded',
            st_null_timers: 'Reset all timers',
            st_gv_n_time: 'Set TG/RG timer for once to',
            st_percent_faster: 'Quests HG, MG, TG, RG more often',
            st_gre_check: 'Immediately initiate Rangers\' guild battle on arrival',
            st_show_timers: 'Show timers:',
            st_predupr_pa: '<b>Abu-Bakir\'s Charm</b> is detected automatically',
            st_work_trudogolik_show: 'Notify about workaholic penalty only 2 workshifts away',
            st_work_trudogolik_off: 'Turn off all notifications on workaholic penalty',
            st_predupr_go_lic: '<b>Hunter license</b> is detected automatically in Hunters\' Guild',
            st_go_timer_hide: 'Hide',
            st_disable_multiple_alarms: 'Disable repeat signals for ' + disable_alarm_delay + ' sec',
            workaholic_penalty: 'Workaholic penalty',
            workaholic_penalty_regexp: 'workaholic penalty',
            regexp_sm: /Completion time: (\d+)-(\d+) (\d+):(\d+)/,
            workaholic_text1: ' approximately through ',
            workaholic_text2: ' enrollments.',
            workaholic_text3: '',
            workaholic_text1_replace: ' <font color:"red">enabled</font> approximately ',
            uze_ustroen: 'You are already employed\.',
            uze_ustroen2: 'Less than one hour passed since last enrollment\. Please wait\.',
            uze_ustroen3: 'No vacancies\.'
        };
    } else {
        obj = {
            health_alert_ty: 'Будет предупреждение о восстановлении армии',
            health_alert_tn: 'Установить единоразово предупреждение о восстановлении армии',
            work_alert_ty: 'Будет предупреждение о конце рабочего часа',
            work_alert_tn: 'Не будет предупреждения',
            sm_alert_ty: 'Будет предупреждение о завершении работ в Кузнице',
            gn_alert_ty: 'Будет предупреждение Гильдии Наемников',
            regexp_timegn0: /Приходи через (\d+) мин/,
            regexp_timegn1: /Осталось времени: (\d+) минут/,
            regexp_timegn2: /тебя осталось (\d+) минут/,
            regexp_timegn3: /у тебя еще есть (\d+) минут/,
            regexp_timegn4: /\. Осталось (\d+) минут\./,
            regexp_timegn5: /осталось \d+ попыток и (\d+) минут/,
            regexp_gn_rep: /Репутация: <b>([\d\.]+)/,
            go_alert_ty: 'Будет предупреждение Гильдии Охотников',
            regexp_go_timer: 'Следующая охота будет доступна через',
            gv_alert_ty: 'Будет предупреждение Гильдии Воров',
            gre_alert_ty: 'Будет предупреждение Гильдии Рейнджеров',
            regexp_timegre: /приходи через (\d+) мин/,
            time_home: /Вы можете устроиться на работу через (\d+)/,
            time_home2: / с (\d{1,2}):(\d{1,2})/,
            workPlace: 'Место работы:',
            alert_health: 'Готовность армии: 100%',
            alert_work: 'ГР: Пора на работу',
            alert_sm: 'ГК: Работа в Кузнице завершена',
            alert_gn: 'ГН: Для Вас есть задание в Гильдии Наемников',
            alert_go: 'ГО: Вы увидели следы ...',
            alert_gv: 'ГВ: Вы можете устроить засаду',
            alert_gre: 'ГРж: Есть задание в Гильдии Рейнджеров',
            audio_file: 'Звук сигнала ',
            alarm_mode: '<b>Режим оповещения</b> окончания таймера:',
            alarm_mode_sound: 'звук',
            alarm_mode_alert: 'сообщение',
            alarm_mode_both: 'оповещение',
            alarm_mode_none: 'отключен',
            h_t: 'здоровья',
            gonv_t: 'ГОНВ(Рж)',
            gr_t: 'ГР',
            gk_t: 'ГК',
            gk_title: 'В Кузницу',
            gn_t: 'ГН',
            gn_title: 'В здание Гильдии Наемников',
            go_t: 'ГО',
            go_title: 'В здание Гильдии Охотников',
            gv_t: 'ГВ',
            gv_title: 'В здание Гильдии Воров',
            gre_t: 'ГРж',
            gre_title: 'В здание Гильдии Рейнджеров',
            mana_title: 'Настройки',
            work_obj_do: 'Вы устроены на работу',
            work_unemployed: 'Вы нигде не работаете',
            regexp_map_go: 'Во время пути Вам доступны',
            go_title_lic: 'Лицензия истекает ',
            alert_go_lic_exp: 'ГО: Лицензия охотника истекла',
            alert_prem_exp: 'Благословение Абу-Бекра истекло',
            workaholic_penalty: 'Штраф трудоголика',
            workaholic_penalty_regexp: 'штраф трудоголика',
            regexp_sm: /Завершение работы: (\d+)-(\d+) (\d+):(\d+)/,
            st_start: 'Все изменения будут видны после перезагрузки страницы',
            st_null_timers: 'Обнулить все таймеры',
            st_gv_n_time: 'Единоразово установить таймер ГВ/ГРж равным',
            st_percent_faster: 'Задания ГО, ГН, ГВ, ГРж чаще на',
            st_gre_check: 'По прибытии вступать в бои Гильдии Рейнджеров',
            st_show_timers: '<b>Отображать:</b>',
            st_predupr_pa: '<b>Благословение Абу-Бекра</b> определяется автоматически',
            st_work_trudogolik_show: '<b>Показывать</b> штраф трудоголика только <b>за 2 часа</b>',
            st_work_trudogolik_off: '<b>Отключить</b> ВСЕ уведомления о штрафе трудоголика',
            st_predupr_go_lic: '<b>Лицензия охотника</b> определяется автоматически (в Гильдии Охотников)',
            st_go_timer_hide: '<b>Скрывать</b>',
            st_disable_multiple_alarms: 'Запретить повторные сигналы в течении ' + disable_alarm_delay + ' секунд',
            workaholic_text1: ' примерно через ',
            workaholic_text2: ' устройств',
            workaholic_text3: ' на работу.',
            workaholic_text1_replace: ' <font color:"red">активен</font> примерно ',
            uze_ustroen: 'Вы уже устроены\.',
            uze_ustroen2: 'Прошло меньше часа с последнего устройства на работу\. Ждите\.',
            uze_ustroen3: 'Нет рабочих мест\.'
        };
    }

    obj.regexp_time_server = /(\d+):(\d+), \d+ online/;
    obj.regexp_time_server2 = /(\d+):(\d+):(\d+), \d+ online/;
    obj.regexp_lic_mo = /(\d+)-(\d+)-(\d+) (\d+):(\d+)/;
    obj.regexp_prem = /(\d+)-(\d+)-(\d+) (\d+):(\d+)/;
    obj.sm_alert_tn = obj.work_alert_tn;
    obj.gn_alert_tn = obj.work_alert_tn;
    obj.go_alert_tn = obj.work_alert_tn;
    obj.gv_alert_tn = obj.work_alert_tn;
    obj.gre_alert_tn = obj.work_alert_tn;
    obj.gv_tit = '/thief_guild.php';
    obj.gre_tit = '/ranger_guild.php';

    if(options.gv_or_gre == '1') {
        obj.alert_gv = obj.alert_gre;
        obj.gv_alert_ty = obj.gre_alert_ty;
        obj.gv_alert_tn = obj.gre_alert_tn;
        obj.gv_t = obj.gre_t;
        obj.gv_title = obj.gre_title;
        obj.gv_tit = obj.gre_tit;
    }
    return obj;
}
function createTimersPanel() {
    const canvasHeart = document.querySelector("canvas#heart");
    const canvasMana = document.querySelector("canvas#mana");
    
    const shContainer = document.querySelector("div.sh_container");
    const sh_MenuPanel = document.querySelector("div.sh_MenuPanel");
    
    const mainTopTable = document.querySelector("table#main_top_table");
    const capContainer = mainTopTable || shContainer;
    
    const backgroundColor = isNewInterface ? "linear-gradient(to top, #09203f 0%, #537895 100%)" : (document.querySelector("img[src*='i/top_ny']") ? "#003399" : "#6b6b69");
    const foreColor = "#f5c137";
    const cellStyle = `background: ${backgroundColor}; color: ${foreColor};`;


    // const timersTable = addElement("table", capContainer, { style: "position: absolute;" });
    // const timersRow = addElement("tr", timersTable);
    
    // const healthCell = addElement("td", timersRow, { id: "healthTimerPanel", style: cellStyle });
    const dragonLeft = document.querySelector("img[src*='i/top'][src*='/dragon__left']"); // https://dcdn1.heroeswm.ru/i/top_ny_rus/dragon__left_.jpg
    const dragonRight = document.querySelector("img[src*='i/top'][src*='/dragon__right']");
    
    //document.querySelector("img.mm_decor1") // https://dcdn.heroeswm.ru/i/new_top_ny/mm_decor1.png
    
    let folder = "https://dcdn2.heroeswm.ru/i/top_ny_rus/line/";
    const img_link = document.querySelector("img[src*='i/top'][src*='/line/t_end']");
    if(img_link) {
        folder = /(\S*\/line\/)/.exec(img_link.src)[1]; 
    }
    const newYearSuffix = document.querySelector("img[src*='i/top_ny']") || document.querySelector("img[src*='i/new_top_ny']") ? "_" : ""; // если новый год
    //console.log(`folder: ${folder}, newYearSuffix: ${newYearSuffix}, ${folder}t_end${newYearSuffix}.jpg`); // folder: https://dcdn2.heroeswm.ru/i/top_ny_rus/line/, newYearSuffix: _
    
    let container = dragonLeft?.parentNode || sh_MenuPanel;
    let timersContainerWidth;
    let zIndex = 0;
    let leftMargin = 0;
    let topMargin = 0;
    let height;
    if(isNewInterface) {
        console.log(`width: ${shContainer.getBoundingClientRect().width}, paddingLeft: ${shContainer.style.paddingRight}`);
        timersContainerWidth = shContainer.getBoundingClientRect().width - 30 * 2;
        topMargin = -67;
        leftMargin = 159;
        zIndex = 2;
        height = 26;
        //GM_addStyle(`.noTransition { -webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important; }`);
        //Array.from(document.querySelectorAll(".mm_item")).forEach(x => { x.classList.add('noTransition'); x.style.marginTop = "18px"; }); window.innerHeight; // Не получилось убрать плавный сдвиг меню вниз
        document.querySelector("div#ResourcesPanel").style.height = "20px";
    } else {
        timersContainerWidth = dragonRight.getBoundingClientRect().left - dragonLeft.getBoundingClientRect().left + 124;
        topMargin = -26;
        leftMargin = -43;
        height = 26;
    }
    const timersHtml = `
<style>
    .hwm_tb * {font-size: 11px; color: #f5c137;}
    .hwm_tb_cell {border-collapse: collapse; background-color: #6b6b69;}
    .hwm_tb_cell TD {padding: 0px;}
    .cell_t {height: 3px; background: url(${folder}t_top_bkg${newYearSuffix}.jpg);}
    .cell_c {white-space: nowrap; height: 18px; background: url(${folder}t_com_bkg${newYearSuffix}.jpg); font-weight: bold;}
    .cell_b {height: 5px; background: url(${folder}t_bot_bkg${newYearSuffix}.jpg); text-align: center;}
    .cell_b IMG {width: 17px; height: 5px;}
</style>
<table cellpadding=0 cellspacing=0 align="center" class="hwm_tb" style="width: ${timersContainerWidth}px;">
    <tr>
        <td>
            <table width="100%" cellpadding=0 cellspacing=0 style="background: url(${folder}t_bkg${newYearSuffix}.jpg);">
                <tr valign=middle align=center>
                    <td width=5 style="overflow: hidden;">
                        <img src="${folder}t_end${newYearSuffix}.jpg" alt="" width=9 height=${height}px style="margin:0px 0px 0px -4px;">
                    </td>
                    <td width=44>
                        <table class="hwm_tb_cell">
                            <tr>
                                <td class="cell_t" />
                            </tr>
                            <tr>
                                <td class="cell_c" style="cursor: pointer;" id="healthTimerPanel">00:00</td>
                            </tr>
                            <tr>
                                <td class="cell_b">
                                    <img src="${folder}t_center${newYearSuffix}.jpg">
                                </td>
                            </tr>
                        </table>
                    </td>
                    <td width=9>
                        <img src="${folder}t_end${newYearSuffix}.jpg" alt="" width=9 height=${height}px>
                    </td>
                    <td id="gr_show1">
                        <table class="hwm_tb_cell">
                            <tr>
                                <td class="cell_t" />
                            </tr>
                            <tr>
                                <td class="cell_c">
                                    <span style="cursor: pointer" id="workTimerPanelCaption">${texts.gr_t}</span>: 
                                    <a id="workTimerPanel" href="${GM_getValue(`LastWorkObjectId${PlayerId}`) ? `object-info.php?id=${GM_getValue(`LastWorkObjectId${PlayerId}`)}` : 'javascript:void(0);'}" style="text-decoration: none;">00:00</a>
                                </td>
                            </tr>
                            <tr>
                                <td class="cell_b">
                                    <img src="${folder}t_center${newYearSuffix}.jpg">
                                </td>
                            </tr>
                        </table>
                    </td>
                    <td id="gr_show2" width=9>
                        <img src="${folder}t_end${newYearSuffix}.jpg" alt="" width=9 height=${height}px>
                    </td>
                    <td id="gk_show1">
                        <table class="hwm_tb_cell">
                            <tr>
                                <td class="cell_t" />
                            </tr>
                            <tr>
                                <td class="cell_c">
                                    <span style="cursor:pointer" id="smithTimerPanelCaption">${texts.gk_t}</span>: 
                                    <a href="/mod_workbench.php?type=repair" title="${texts.gk_title}" style="text-decoration: none;" id="smithTimerPanel">00:00</a>
                                </td>
                            </tr>
                            <tr>
                                <td class="cell_b">
                                    <img src="${folder}t_center${newYearSuffix}.jpg">
                                </td>
                            </tr>
                        </table>
                    </td>
                    <td id="gk_show2" width=9>
                        <img src="${folder}t_end${newYearSuffix}.jpg" alt="" width=9 height=${height}px>
                    </td>
                    <td id="gn_show1">
                        <table class="hwm_tb_cell">
                            <tr>
                                <td class="cell_t" />
                            </tr>
                            <tr>
                                <td class="cell_c">
                                    <span style="cursor:pointer" id="mercTimerPanelCaption">${texts.gn_t}</span>: 
                                    <a href="/mercenary_guild.php" title="${texts.gn_title}" style="text-decoration: none;" id="mercTimerPanel">00:00</a>
                                </td>
                            </tr>
                            <tr>
                                <td class="cell_b">
                                    <img src="${folder}t_center${newYearSuffix}.jpg">
                                </td>
                            </tr>
                        </table>
                    </td>
                    <td id="gn_show2" width=9>
                        <img src="${folder}t_end${newYearSuffix}.jpg" alt="" width=9 height=${height}px>
                    </td>
                    <td id="go_show1">
                        <table class="hwm_tb_cell">
                            <tr>
                                <td class="cell_t" />
                            </tr>
                            <tr>
                                <td class="cell_c">
                                    <span style="cursor: pointer;" id="huntTimerPanelCaption"></span>: 
                                    <a href="/hunter_guild.php" style="text-decoration: none;" id="huntTimerPanel">00:00</a>
                                </td>
                            </tr>
                            <tr>
                                <td class="cell_b">
                                    <img src="${folder}t_center${newYearSuffix}.jpg">
                                </td>
                            </tr>
                        </table>
                    </td>
                    <td id="go_show2" width=9>
                        <img src="${folder}t_end${newYearSuffix}.jpg" alt="" width=9 height=${height}px>
                    </td>
                    <td id="gv_show1">
                        <table class="hwm_tb_cell">
                            <tr>
                                <td class="cell_t" />
                            </tr>
                            <tr>
                                <td class="cell_c">
                                    <span style="cursor:pointer" id="thiefTimerPanelCaption">${texts.gv_t}</span>: 
                                    <a href="${texts.gv_tit}" title="${texts.gv_title}" style="text-decoration: none;" id="thiefTimerPanel">00:00</a>
                                </td>
                            </tr>
                            <tr>
                                <td class="cell_b">
                                    <img src="${folder}t_center${newYearSuffix}.jpg">
                                </td>
                            </tr>
                        </table>
                    </td>
                    <td id="gv_show2" width=9>
                        <img src="${folder}t_end${newYearSuffix}.jpg" alt="" width=9 height=${height}px>
                    </td>
                    <td width=44>
                        <table class="hwm_tb_cell">
                            <tr>
                                <td class="cell_t" />
                            </tr>
                            <tr>
                                <td class="cell_c" style="cursor: pointer;" id="manaTimerPanel" title="${texts.mana_title}">00:00</td>
                            </tr>
                            <tr>
                                <td class="cell_b">
                                    <img src="${folder}t_center${newYearSuffix}.jpg">
                                </td>
                            </tr>
                        </table>
                    </td>
                    <td width=5 style="overflow: hidden;">
                        <img src="${folder}t_end${newYearSuffix}.jpg" alt="" width=9 height=${height}px style="margin:0px -4px 0px 0px;">
                    </td>
                </tr>
            </table>
        </td>
    </tr>
</table>`;
    const timersPanel = addElement("div", container, { style: `position: absolute; margin: ${topMargin}px 0px 0px ${leftMargin}px; text-align: center; z-index: ${zIndex};`, innerHTML: timersHtml });
    timersPanel.querySelector("#manaTimerPanel").addEventListener("click", settings);
    timersPanel.querySelector('#healthTimerPanel').addEventListener("click", function() { updateOption("time_health_alert", options.time_health_alert == 'yes' ? 'no' : 'yes'); bindTimersPanel(); });
    timersPanel.querySelector('#workTimerPanelCaption').addEventListener("click", function() { updateOption("time_work_alert", options.time_work_alert == 'yes' ? 'no' : 'yes'); bindTimersPanel(); });
    timersPanel.querySelector('#smithTimerPanelCaption').addEventListener("click", function() { updateOption("time_sm_alert", options.time_sm_alert == 'yes' ? 'no' : 'yes'); bindTimersPanel(); });
    timersPanel.querySelector('#mercTimerPanelCaption').addEventListener("click", function() { updateOption("time_gn_alert", options.time_gn_alert == 'yes' ? 'no' : 'yes'); bindTimersPanel(); });
    timersPanel.querySelector('#huntTimerPanelCaption').addEventListener("click", function() { updateOption("time_go_alert", options.time_go_alert == 'yes' ? 'no' : 'yes'); bindTimersPanel(); });
    timersPanel.querySelector('#thiefTimerPanelCaption').addEventListener("click", function() { updateOption("time_gv_alert", options.time_gv_alert == 'yes' ? 'no' : 'yes'); bindTimersPanel(); });
}
function bindTimersPanel() {
    const workTimerPanelCaption = document.getElementById('workTimerPanelCaption');
    workTimerPanelCaption.style.color = options.time_work_alert == 'yes' ? '#FF0000' : '#f5c137';
    workTimerPanelCaption.title = options.time_work_alert == 'yes' ?  texts.work_alert_ty : texts.work_alert_tn;
    const workTimerPanel = document.getElementById('workTimerPanel');
    let workTimerPanelTtitle = options.time_percent_prem_title;
    if(options.time_work_trudogolik) {
        if(workTimerPanelTtitle) {
            workTimerPanelTtitle += '\n';
        }
        workTimerPanelTtitle += texts.workaholic_penalty + ": " + (11 - Number(options.time_work_trudogolik));
        if(options.time_work_trudogolik_off == '0' && options.time_work_trudogolik > 10) {
            workTimerPanel.style.color = '#ff9c00';
        }
    }
    workTimerPanel.title = workTimerPanelTtitle;
    
    const healthTimerPanel = document.getElementById('healthTimerPanel');
    healthTimerPanel.style.color = options.time_health_alert == 'yes' ? '#ff9c00' : '#f5c137';
    healthTimerPanel.title = options.time_health_alert == 'yes' ?  texts.health_alert_ty : texts.health_alert_tn;
    
    const smithTimerPanelCaption = document.getElementById('smithTimerPanelCaption');
    smithTimerPanelCaption.style.color = options.time_sm_alert == 'yes' ? '#FF0000' : '#f5c137';
    smithTimerPanelCaption.title = options.time_sm_alert == 'yes' ?  texts.sm_alert_ty : texts.sm_alert_tn;
    const mercTimerPanelCaption = document.getElementById('mercTimerPanelCaption');
    mercTimerPanelCaption.style.color = options.time_gn_alert == 'yes' ? '#FF0000' : '#f5c137';
    mercTimerPanelCaption.title = options.time_gn_alert == 'yes' ?  texts.gn_alert_ty : texts.gn_alert_tn;

    const huntTimerPanelCaption = document.getElementById('huntTimerPanelCaption');
    huntTimerPanelCaption.innerText = texts.go_t;
    huntTimerPanelCaption.style.color = options.time_go_alert == 'yes' ? '#FF0000' : '#f5c137';
    huntTimerPanelCaption.title = options.time_go_alert == 'yes' ?  texts.go_alert_ty : texts.go_alert_tn;
    const huntTimerPanel = document.getElementById('huntTimerPanel');
    let huntTimerPanelTitle = texts.go_title;
    if(texts.go_title_lic) {
        huntTimerPanelTitle += '\n' + texts.go_title_lic + options.time_percent_lic_mo_title;
    }
    huntTimerPanel.title = huntTimerPanelTitle;

    const thiefTimerPanelCaption = document.getElementById('thiefTimerPanelCaption');
    thiefTimerPanelCaption.style.color = options.time_gv_alert == 'yes' ? '#FF0000' : '#f5c137';
    thiefTimerPanelCaption.title = options.time_gv_alert == 'yes' ?  texts.gv_alert_ty : texts.gv_alert_tn;
    
    document.getElementById("gr_show1").style.display = document.getElementById("gr_show2").style.display = (options.isShowWorkTimer == "1" ? '' : "none");
    document.getElementById("gk_show1").style.display = document.getElementById("gk_show2").style.display = (options.isShowSmithTimer == "1" ? '' : "none");
    document.getElementById("gn_show1").style.display = document.getElementById("gn_show2").style.display = (options.isShowMercTimer == "1" ? '' : "none");
    document.getElementById("go_show1").style.display = document.getElementById("go_show2").style.display = (options.isShowHuntTimer == "1" ? '' : "none");
    document.getElementById("gv_show1").style.display = document.getElementById("gv_show2").style.display = (options.isShowThiefTimer == "1" ? '' : "none");
}
function loadWorkEndTime() {
    if(location.pathname == '/home.php') {
        if(document.body.innerHTML.match(texts.work_unemployed)) {
            GM_deleteValue(`WorkEnd${PlayerId}`);
            return;
        }
        let newWorkEnd;
        var gr_temp;
        // подхватывание времени окончания работы с home.php и его проверка
        var time_home_time = texts.time_home.exec(document.body.innerHTML);
        const workEnd = parseInt(GM_getValue(`WorkEnd${PlayerId}`, 0));
        if(time_home_time) {
            newWorkEnd = getServerTime() + Number(time_home_time[1]) * 60000;
        } else {
            time_home_time = texts.time_home2.exec(document.body.innerHTML)
            if(time_home_time) {
                const serverDateTime = getServerDateTime();
                let workBegin = new Date(serverDateTime.getFullYear(), serverDateTime.getMonth(), serverDateTime.getDate(), Number(time_home_time[1]), Number(time_home_time[2]), 0);
                if(workBegin > serverDateTime) {
                    workBegin.setDate(workBegin.getDate() - 1);
                }
                newWorkEnd = workBegin.getTime() + 60 * 60 * 1000;
            }
        }
        if(newWorkEnd && (workEnd == 0 || Math.abs(workEnd - newWorkEnd) > 70000)) {
            GM_setValue(`WorkEnd${PlayerId}`, newWorkEnd);
        }
    }
}
function checkPremiumTime() {
    // проверка наличия эффекта блага АБУ Бекра (премиум аккаунт)
    // skipn=1 это 'Ознакомился' или 'Got it!'
    if(location.pathname == '/home.php' && document.querySelector("img[src*='i/icons/attr_defense.png']") && !document.querySelector("a[href*='home.php?skipn=1']")) {
        const img_star_prem = document.querySelector("img[src$='i/star_extend.png']") || document.querySelector("img[src$='i/star.png']");
        options.time_percent_prem = img_star_prem ? "0.7" : "1";
        options.time_percent_prem_exp = '0';
        options.time_percent_prem_title = img_star_prem ? img_star_prem.title : '';
        if(img_star_prem) {
            img_star_prem.align = "absmiddle";
            const time_prem = /(\d+-\d+-\d+ \d+:\d+)/.exec(img_star_prem.title);
            if(time_prem) {
                const abuEnd = parseDate(time_prem[1], true);
                console.log(abuEnd);
                options.time_percent_prem_exp = abuEnd.getTime();
            }
        }
    }
    if(options.time_percent_prem_title && Number(options.time_percent_prem_exp) < serverNow()) {
        options.time_percent_prem = '1';
        options.time_percent_prem_exp = '0';
        options.time_percent_prem_title = '';
    }
}
function checkLicMoO() {
    var form_f2 = document.querySelector("form[name='f2']");
    if(location.pathname == '/hunter_guild.php' && form_f2) {
        while(form_f2.tagName != 'TR') {
            form_f2 = form_f2.parentNode;
        }
        options.time_percent_lic_mo = '1';
        options.time_percent_lic_mo_exp = '0';
        options.time_percent_lic_mo_title = '';
        if(texts.regexp_lic_mo.exec(form_f2.innerHTML)) {
            if(!form_f2.querySelector("input[type='submit'][onclick*='confirm']")) {
                // лицензия МО
                options.time_percent_lic_mo = '' + (50 / 100);
            } else {
                // лицензия О
                options.time_percent_lic_mo = '' + (75 / 100);
            }
            const forms = form_f2.querySelectorAll("td");
            var time_lic_mo_max = 0;
            for(const form of forms) {
                if(form.innerHTML.indexOf("<td") != -1) {
                    continue;
                }
                var time_lic_mo = /(\d+-\d+-\d+ \d+:\d+)/.exec(form.innerHTML);
                if(time_lic_mo) {
                    const licEndTime = parseDate(time_lic_mo[1], true).getTime();
                    if(licEndTime > time_lic_mo_max) {
                        time_lic_mo_max = licEndTime;
                        options.time_percent_lic_mo_exp = licEndTime;
                        options.time_percent_lic_mo_title = time_lic_mo[0];
                    }
                }
            }
        }
    }
    if(options.time_percent_lic_mo_title && Number(options.time_percent_lic_mo_exp) < serverNow()) {
        // лицензия охотника истекла
        options.time_percent_lic_mo = '1';
        options.time_percent_lic_mo_exp = '0';
        options.time_percent_lic_mo_title = '';
    }
}
function checkWar() {
    console.log(`checkWar AmbushTimeout: ${GM_getValue(`AmbushTimeout${PlayerId}`)}, map_thief_ambush: ${options.map_thief_ambush}, map_hunter: ${options.map_hunter}, army_percent: ${army_percent}, HoldBattle: ${GM_getValue(`HoldBattle${PlayerId}`)}`);
    if(gmGetBool(`HoldBattle${PlayerId}`)) {
        var bselect_link = document.querySelector("a[href='bselect.php']") || document.querySelector("a[href='plstats.php']");
        if(bselect_link && bselect_link.parentNode.innerHTML.indexOf("#ff0000") === -1) {
            GM_deleteValue(`HoldBattle${PlayerId}`);
            if(options.map_thief_ambush == 'true') {
                if(army_percent < 100) {
                    GM_setValue(`AmbushTimeout${PlayerId}`, Date.now() + 60 * 60000 * options.time_percent_faster * options.time_percent_prem);
                } else {
                    GM_deleteValue(`AmbushTimeout${PlayerId}`);
                }
            }
            if(options.map_hunter == 'true') {
                setHuntTimeout();
            }
            if(army_percent == 100 && options.gl_attack != 'true') {
                options.time_work_trudogolik = '0';
            }
            options.gl_attack = 'false';
            options.map_thief_ambush = 'false';
            options.map_hunter = 'false';
        }
    }
    console.log(`checkWar AmbushTimeout: ${GM_getValue(`AmbushTimeout${PlayerId}`)}`);
}
function setHuntTimeout(restSeconds) {
    restSeconds = restSeconds || ((getServerDateTime().getHours() < 8 ? 20 : 40) * 60 * options.time_percent_faster * options.time_percent_prem * options.time_percent_lic_mo);
    const huntTimeout = Date.now() + restSeconds * 1000;
    GM_setValue(`HuntTimeout${PlayerId}`, huntTimeout);
    launchTimers();
}
function skipHunt() {
    const restSeconds = ((getServerDateTime().getHours() < 8 ? 10 : 20) * 60 * options.time_percent_faster * options.time_percent_prem * options.time_percent_lic_mo)
    setHuntTimeout(restSeconds);
    const map_hunt_block_div = document.querySelector("div#map_hunt_block_div");
    if(map_hunt_block_div) {
        observe(map_hunt_block_div, hideNextHuntTimer, true);
    }
}
function checkWork() {
    if(location.pathname == '/object-info.php') {
        var parent_trud = document.querySelector("a[href*='objectworkers.php']");
        if(parent_trud) {
            var regexp_workaholic = new RegExp('\\*\\&nbsp;0\\.(\\d) ' + texts.workaholic_penalty_regexp);
            // отработано смен
            var workaholic_WORK = Number(options.time_work_trudogolik);
            if(regexp_workaholic.exec(document.body.innerHTML)) {
                regexp_workaholic = Number(regexp_workaholic.exec(document.body.innerHTML)[1]);
                if(regexp_workaholic == 8) {
                    workaholic_WORK = 11;
                } else if(regexp_workaholic == 6) {
                    workaholic_WORK = 12;
                } else if(regexp_workaholic == 4) {
                    workaholic_WORK = 13;
                } else if(regexp_workaholic == 2) {
                    workaholic_WORK = 14;
                } else if(regexp_workaholic == 1 && workaholic_WORK < 15) {
                    workaholic_WORK = 15;
                }
                options.time_work_trudogolik = '' + workaholic_WORK;
            } else if(workaholic_WORK > 10) {
                workaholic_WORK = 10;
                options.time_work_trudogolik = '' + workaholic_WORK;
            }
            var add_trud = document.createElement('span');
            if(workaholic_WORK == 9 || workaholic_WORK == 10) {
                // выделить цветом
                add_trud.setAttribute('style', 'color:red; font-weight:bold;');
            } else if(workaholic_WORK > 10) {
                texts.workaholic_text1 = texts.workaholic_text1_replace;
            }
            // осталось работать
            var workaholic_ENROLL = Math.abs(11 - workaholic_WORK);
            if(workaholic_WORK > 14) {
                workaholic_ENROLL = workaholic_ENROLL + '+';
            }
            // правильные окончания слов
            if(!isEn) {
                if(workaholic_WORK == 9 || workaholic_WORK == 8 || workaholic_WORK == 7) {
                    texts.workaholic_text2 += '\u0430';
                } else if(workaholic_WORK == 10) {
                    texts.workaholic_text2 += '\u043E';
                }
            }
            if(options.time_work_trudogolik_off == '0') {
                if(options.time_work_trudogolik_show == '1' && workaholic_WORK != 9 && workaholic_WORK != 10) {} else {
                    add_trud.innerHTML = texts.workaholic_penalty + texts.workaholic_text1 + workaholic_ENROLL + texts.workaholic_text2 + texts.workaholic_text3;
                    parent_trud = parent_trud.parentNode.previousSibling.previousSibling;
                    parent_trud.parentNode.insertBefore(add_trud, parent_trud);
                }
            }
            // замена "Уже устроен"
            parent_trud = document.querySelector("a[href*='objectworkers.php']").parentNode.parentNode;
            if(Date.now() > parseInt(GM_getValue(`WorkEnd${PlayerId}`, 0)) && (parent_trud.innerHTML.match(texts.uze_ustroen) || (texts.uze_ustroen = parent_trud.innerHTML.match(texts.uze_ustroen2)) || (texts.uze_ustroen = parent_trud.innerHTML.match(texts.uze_ustroen3)))) {
                parent_trud.innerHTML = parent_trud.innerHTML.replace(texts.uze_ustroen, '<style>@-webkit-keyframes blink {80% {opacity:0.0;}} @-moz-keyframes blink {80% {opacity:0.0;}} @-o-keyframes blink {80% {opacity:0.0;}} @keyframes blink {80% {opacity:0.0;}}</style><font color=blue style="-webkit-animation: blink 1s steps(1,end) 0s infinite; -moz-animation: blink 1s steps(1,end) 0s infinite; -o-animation: blink 1s steps(1,end) 0s infinite; animation: blink 1s steps(1,end) 0s infinite"><b>' + texts.uze_ustroen + '</b></font>');
            }
        }
    }
}
function checkMercenary() {
    if(location.pathname == '/mercenary_guild.php') {
        var time_gn;
        if(document.querySelector("a[href^='/mercenary_guild.php?action=accept']")) {
            GM_deleteValue(`MercenaryTimeout${PlayerId}`);
        } else if((time_gn = texts.regexp_timegn0.exec(document.body.innerHTML)) || (time_gn = texts.regexp_timegn1.exec(document.body.innerHTML)) || (time_gn = texts.regexp_timegn2.exec(document.body.innerHTML)) || (time_gn = texts.regexp_timegn3.exec(document.body.innerHTML)) || (time_gn = texts.regexp_timegn4.exec(document.body.innerHTML)) || (time_gn = texts.regexp_timegn5.exec(document.body.innerHTML))) {
            time_gn = Number(time_gn[1]);
            if(texts.regexp_timegn0.exec(document.body.innerHTML) && (time_gn == 19 || time_gn == 13))
                time_gn++;
            time_gn = time_gn * 60000; // в миллисекундах
            const now = Date.now();
            var time_gn_temp = time_gn - Math.abs(parseInt(GM_getValue(`MercenaryTimeout${PlayerId}`, 0)) - now);
            if(Math.abs(time_gn_temp) > 70000) {
                var reputation_gn = texts.regexp_gn_rep.exec(document.body.innerHTML);
                reputation_gn = (40 - Number(reputation_gn[1]) * 2) * options.time_percent_faster * options.time_percent_prem * 60000;
                // в миллисекундах
                time_gn_temp = time_gn - reputation_gn;
                GM_setValue(`MercenaryTimeout${PlayerId}`, now + (Math.abs(time_gn_temp) > 70000 ? time_gn : reputation_gn))
            }
        }
        // options.grandma = '1';
        if(document.body.innerHTML.match('¬ы получаете') || document.body.innerHTML.match('You receive')) {
            var flash_heart = document.querySelector("object > param[value*='mercenary.swf']");
            if(flash_heart) {
                var rand_f;
                if(new Date().getHours() == 23) {
                    rand_f = "d8EWAZm.jpg";
                } else if(options.grandma) {
                    var img_win = new Array("3xVyD9G.jpg", "rdc2phi.jpg", "4Sz0fZh.jpg", "EeSup0D.jpg", "cfqFars.jpg", "HCuDAHi.jpg", "pYaFMyE.jpg");
                    rand_f = Math.floor(Math.random() * img_win.length);
                    rand_f = img_win[rand_f];
                }
                if(rand_f) {
                    flash_heart.parentNode.style.display = 'none';
                    var add_el = document.createElement('img');
                    add_el.height = "150";
                    add_el.width = "150";
                    add_el.src = "https://i.imgur.com/" + rand_f;
                    flash_heart.parentNode.parentNode.appendChild(add_el);
                }
            }
        }
    }
}
function checkRangerGuild() {
    if(location.pathname == '/ranger_guild.php') {
        if(document.querySelector("a[href^='ranger_guild.php?action=accept']")) {
            options.map_thief_ambush = 'false';
            GM_deleteValue(`AmbushTimeout${PlayerId}`)
            options.gv_or_gre = '1';
        }
        var time_gv = texts.regexp_timegre.exec(b.innerHTML);
        if(time_gv) {
            time_gv = Number(time_gv[1]) * 60000; // в миллисекундах
            const now = Date.now();
            var time_gv_temp = time_gv - Math.abs(parseInt(GM_getValue(`AmbushTimeout${PlayerId}`, 0)) - now);
            if(Math.abs(time_gv_temp) > 70000) {
                options.map_thief_ambush = 'false';
                GM_setValue(`AmbushTimeout${PlayerId}`, now + time_gv)
                options.gv_or_gre = '1';
            }
        }
    }
    if(location.pathname == '/ranger_list.php') {
        var link_ranger_attack = document.querySelectorAll("a[href^='ranger_attack.php?join']");
        if(link_ranger_attack.length > 0) {
            options.map_thief_ambush = 'false';
            GM_deleteValue(`AmbushTimeout${PlayerId}`)
            options.gv_or_gre = '1';
            for(const link_ranger_attackItem of link_ranger_attack) {
                link_ranger_attackItem.addEventListener("click", set_thief_ambush);
            }
        }
    }
}
function checkModWorkebench() {
    if(location.pathname == '/mod_workbench.php') {
        parseSmithPage(document);
    }
}
function parseSmithPage(doc) {
    const allb = doc.querySelectorAll("b");
    for(const bold of allb) {
        if(bold.innerText.includes(isEn ? "Under repair" : "В ремонте")) {
            var repairData = bold.innerText;
            break;
        }
    }
    if(repairData) {
        const restRepairTime = { Hours: 0, Minutes: 0, Seconds: 59 };
        //В ремонте: еще 1 ч. 31 мин. //Under repair another 1 h. 17 min.
        const hoursRegex = new RegExp(`(\\d+) ${isEn ? "h" : "ч"}\\.`);
        const hoursRegexResult = hoursRegex.exec(repairData);
        if(hoursRegexResult) {
            restRepairTime.Hours = parseInt(hoursRegexResult[1]);
        }
        const minutesRegex = new RegExp(`(\\d+) ${isEn ? "min" : "мин"}\\.`);
        const minutesRegexResult = minutesRegex.exec(repairData);
        if(minutesRegexResult) {
            restRepairTime.Minutes = parseInt(minutesRegexResult[1]);
        }
        //console.log(repairData);
        //console.log(restRepairTime);
        const repairEnd = new Date();
        repairEnd.setHours(repairEnd.getHours() + restRepairTime.Hours);
        repairEnd.setMinutes(repairEnd.getMinutes() + restRepairTime.Minutes);
        repairEnd.setSeconds(repairEnd.getSeconds() + restRepairTime.Seconds);
        //console.log(repairEnd);
        
        const savedRepairEnd = GM_getValue(`RepairEnd${PlayerId}`);
        if(!savedRepairEnd || Math.abs(repairEnd.getTime() - parseInt(savedRepairEnd)) / 1000 / 60 > 2) {
            GM_setValue(`RepairEnd${PlayerId}`, repairEnd.getTime());
        }
    } else {
        GM_deleteValue(`RepairEnd${PlayerId}`);
    }
}
function checkMapThief() {
    console.log(`checkMapThief map_thief_ambush: ${options.map_thief_ambush}, gv_or_gre: ${options.gv_or_gre}`);
    var thief_ambush_cancel = document.querySelector("a[href^='thief_ambush_cancel.php']");
    if(thief_ambush_cancel) {
        options.map_thief_ambush = 'true';
        GM_deleteValue(`AmbushTimeout${PlayerId}`);
        options.gv_or_gre = '0';
        thief_ambush_cancel.addEventListener("click", function(event) { updateOption("map_thief_ambush", 'false'); });
    }
    if(document.querySelector("a[href='ecostat.php']")) {
        if(options.gv_or_gre == '0' && !thief_ambush_cancel) {
            options.map_thief_ambush = 'false';
        }
        if(options.gv_or_gre == '1' && !document.querySelector("a[href='ranger_guild.php']")) {
            options.map_thief_ambush = 'false';
        }
    }
    var form_thief_ambush = document.querySelector("form[action='thief_ambush.php']");
    if(form_thief_ambush) {
        options.map_thief_ambush = 'false';
        GM_deleteValue(`AmbushTimeout${PlayerId}`);
        options.gv_or_gre = '0';
        var input_form_thief_ambush = form_thief_ambush.querySelector("input[type='submit']");
        input_form_thief_ambush.addEventListener("click", set_thief_ambush);
    }
    console.log(`checkMapThief map_thief_ambush: ${options.map_thief_ambush}, gv_or_gre: ${options.gv_or_gre}`);
}
function checkMapRanger() {
    var form_ranger_attack = document.querySelector("form[action='ranger_attack.php']");
    if(form_ranger_attack) {
        options.map_thief_ambush = 'false';
        GM_deleteValue(`AmbushTimeout${PlayerId}`);
        options.gv_or_gre = '1';
        form_ranger_attack.querySelector("input[type='submit']").addEventListener("click", function() { updateOption("map_thief_ambush", 'true'); });
        if(options.gre_check == '1') {
            options.map_thief_ambush = 'true';
            setTimeout(function() { form_ranger_attack.submit(); }, 500);
        }
    }
}
function checkMapHunter() {
    const map_hunt_block_div = document.querySelector("div#map_hunt_block_div");
    if(map_hunt_block_div) {
        const next_ht_new = map_hunt_block_div.querySelector("div#next_ht_new");
        options.map_hunter = 'false';
        if(next_ht_new) {
            setHuntTimeout(windowObject.MapHunterDelta);
        } else {
            GM_deleteValue(`HuntTimeout${PlayerId}`);
            const attackButtons = map_hunt_block_div.querySelectorAll("div[hint='Напасть']");
            for(const button of attackButtons) {
                button.addEventListener("click", function() { updateOption("map_hunter", 'true'); }, false);
            }
            const skipButtons = map_hunt_block_div.querySelectorAll("div[hint^='Пройти']");
            for(const button of skipButtons) {
                button.addEventListener("click", function() { skipHunt(); }, false);
            }
            const callButtons = map_hunt_block_div.querySelectorAll("div[hint^='Позвать']");
            for(const button of callButtons) {
                button.addEventListener("click", function() { setTimeout(onclick_hunter_help, 200); }, false);
            }
        }
    }
    if(mooving && !GM_getValue(`HuntTimeout${PlayerId}`)) {
        skipHunt();
    }
    hideNextHuntTimer();
}
function hideNextHuntTimer() {
    const next_ht_new = document.querySelector("div#next_ht_new");
    if(next_ht_new && gmGetBool("HideHuntTimer")) {
        document.querySelector("div#map_hunt_block_div").style.display = "none";
    }
}
function parseWaitTime(timeText) {
    const restTime = parseTime(timeText);
    const date = new Date();
    date.setHours(date.getHours() + restTime.Hours);
    date.setMinutes(date.getMinutes() + restTime.Minutes);
    date.setSeconds(date.getSeconds() + restTime.Seconds);
    return date;
}
function parseTime(timeText) {
    const time = { Hours: 0, Minutes: 0, Seconds: 0 };
    const hoursRegex = new RegExp(`(\\d+) ${isEn ? "h" : "ч"}\\.`);
    const hoursRegexResult = hoursRegex.exec(timeText);
    if(hoursRegexResult) {
        time.Hours = parseInt(hoursRegexResult[1]);
    }
    const minutesRegex = new RegExp(`(\\d+) ${isEn ? "min" : "мин"}\\.`);
    const minutesRegexResult = minutesRegex.exec(timeText);
    if(minutesRegexResult) {
        time.Minutes = parseInt(minutesRegexResult[1]);
    }
    const secondsRegex = new RegExp(`(\\d+) ${isEn ? "sec" : "с"}\\.`);
    const secondsRegexResult = secondsRegex.exec(timeText);
    if(secondsRegexResult) {
        time.Seconds = parseInt(secondsRegexResult[1]);
    }
    return time;
}
function onclick_hunter_help() {
    var forms = document.querySelectorAll("form[action='/map.php']");
    for(const form of forms) {
        const input_form_go_link_help = form.querySelector("input[type='submit']");
        input_form_go_link_help.addEventListener("click", function() { updateOption("map_hunter", 'true'); }, false);
    }
}
function set_thief_ambush() {
    updateOption("map_thief_ambush", 'true');
}
function launchTimers() {
    const time = { healthTimerPanel: -1, manaTimerPanel: -1, workTimerPanel: -1, mercTimerPanel: -1, thiefTimerPanel: -1, huntTimerPanel: -1, smithTimerPanel: -1 };
    const now = Date.now();
    if(GM_getValue(`HealthRestoreTime${PlayerId}`)) {
        time.healthTimerPanel = parseInt(GM_getValue(`HealthRestoreTime${PlayerId}`)) - now;
    }
    const workEnd = parseInt(GM_getValue(`WorkEnd${PlayerId}`, 0));
    if(now < workEnd) {
        time.workTimerPanel = workEnd - now;
    } else {
        GM_deleteValue(`WorkEnd${PlayerId}`);
    }
    const repairEnd = parseInt(GM_getValue(`RepairEnd${PlayerId}`, 0));
    if(now < repairEnd) {
        time.smithTimerPanel = repairEnd - now;
    } else {
        GM_deleteValue(`RepairEnd${PlayerId}`);
    }
    const mercenaryTimeout = parseInt(GM_getValue(`MercenaryTimeout${PlayerId}`, 0));
    if(now < mercenaryTimeout) {
        time.mercTimerPanel = mercenaryTimeout - now;
    } else {
        GM_deleteValue(`MercenaryTimeout${PlayerId}`);
    }
    const huntTimeout = parseInt(GM_getValue(`HuntTimeout${PlayerId}`, 0));
    if(now < huntTimeout) {
        time.huntTimerPanel = huntTimeout - now;
    } else {
        GM_deleteValue(`HuntTimeout${PlayerId}`);
    }
    const ambushTimeout = parseInt(GM_getValue(`AmbushTimeout${PlayerId}`, 0));
    if(now < ambushTimeout) {
        time.thiefTimerPanel = ambushTimeout - now;
    } else {
        GM_deleteValue(`AmbushTimeout${PlayerId}`);
    }
    clearTimeout(tickTimer);
    tick(time);
}
function signal(message, sound) {
    if(options.disable_multiple_alarms == '1') {
        if(parseInt(GM_getValue(`LastNotificationTime${PlayerId}`, 0)) + disable_alarm_delay * 1000 > Date.now()) {
            return;
        }
        GM_setValue(`LastNotificationTime${PlayerId}`, Date.now());
    }
    switch(GM_getValue(`NotificationType${PlayerId}`, '0')) {
        case '0':
            sound.play();
            break;
        case '1':
            alert(message);
            break;
        case '2':
            GM.notification(message, "ГВД", "https://dcdn.heroeswm.ru/i/rewards/fast_t/3x3_1.png", function() { window.focus(); });
            break;
        default: //including '3'
    }
}
function milisecondsFormat(milisecondsLeft) {
    const secondsLeft = Math.floor(milisecondsLeft / 1000);
    const days = Math.floor(secondsLeft / 86400);
    const hours = Math.floor((secondsLeft - days * 86400) / 3600);
    const minutes = Math.floor((secondsLeft - days * 86400 - hours * 3600) / 60);
    const seconds = secondsLeft % 60;
    return (days === 0 ? '' : ((days < 10) ? '0' : '') + days + ':') + (days === 0 && hours === 0 ? '' : ((hours < 10) ? '0' : '') + hours + ':') + ((minutes < 10) ? '0' : '') + minutes + ':' + ((seconds < 10) ? '0' : '') + seconds;
}
function tick(time) {
    //console.log(time);
    for(const timerPanelId in time) {
        //console.log(timerPanelId)
        const milisecondsLeft = time[timerPanelId];
        if(milisecondsLeft >= 0) {
            document.getElementById(timerPanelId).innerHTML = milisecondsFormat(milisecondsLeft);
        }
        if(milisecondsLeft > 0) {
            time[timerPanelId] -=  Math.min(1000, milisecondsLeft);
        }
        if(milisecondsLeft == 0) {
            time[timerPanelId] = -1;
            if(timerPanelId == 'healthTimerPanel') {
                var healthTimerPanel = document.getElementById('healthTimerPanel');
                healthTimerPanel.style.color = '#f5c137';
                healthTimerPanel.title = texts.health_alert_tn;
                options = JSON.parse(GM_getValue(`hwmTimersOptions${PlayerId}`));
                if(options.time_health_alert == 'yes') {
                    options.time_health_alert = 'no';
                    GM_setValue(`hwmTimersOptions${PlayerId}`, JSON.stringify(options));
                    setTimeout(function() { signal(texts.alert_health, audio_h); }, 100);
                }
            }
            if(timerPanelId == 'workTimerPanel' && parseInt(GM_getValue(`WorkEnd${PlayerId}`, 0)) > 0 && options.time_work_alert == 'yes') {
                GM_deleteValue(`WorkEnd${PlayerId}`);
                setTimeout(function() { signal(texts.alert_work, audio_gr); }, 100);
            }
            if(timerPanelId == 'smithTimerPanel' && parseInt(GM_getValue(`RepairEnd${PlayerId}`, 0)) > 0 && options.time_sm_alert == 'yes') {
                GM_deleteValue(`RepairEnd${PlayerId}`);
                setTimeout(function() { signal(texts.alert_sm, audio_gk); }, 100);
            }
            if(timerPanelId == 'mercTimerPanel' && parseInt(GM_getValue(`MercenaryTimeout${PlayerId}`, 0)) > 0 && options.time_gn_alert == 'yes') {
                GM_deleteValue(`MercenaryTimeout${PlayerId}`);
                setTimeout(function() { signal(texts.alert_gn, audio_gonv); }, 100);
            }
            if(timerPanelId == 'huntTimerPanel' && parseInt(GM_getValue(`HuntTimeout${PlayerId}`, 0)) > 0 && options.time_go_alert == 'yes') {
                GM_deleteValue(`HuntTimeout${PlayerId}`);
                setTimeout(function() { signal(texts.alert_go, audio_gonv); }, 100);
            }
            if(timerPanelId == 'thiefTimerPanel' && parseInt(GM_getValue(`AmbushTimeout${PlayerId}`, 0)) > 0 && options.time_gv_alert == 'yes') {
                GM_deleteValue(`AmbushTimeout${PlayerId}`);
                setTimeout(function() { signal(texts.alert_gv, audio_gonv); }, 100);
            }
        }
    }
    if(Object.keys(time).reduce((t, c) => Math.max(t, time[c]), -1) > -1) {
        tickTimer = setTimeout(function() { tick(time); }, 1000);
    }
}
function settings() {
    if(showPupupPanel(GM_info.script.name)) {
        return;
    }
    options = JSON.parse(GM_getValue(`hwmTimersOptions${PlayerId}`));
    const bgcinnerHTML = `
<tr>
    <td align="center"> <b>${texts.st_start}</b> </td>
</tr>
<tr>
    <td>${texts.st_show_timers}&nbsp;&nbsp;${texts.gr_t}:<input type=checkbox ${add_checked(options.isShowWorkTimer)} id=isShowWorkTimerCheckbox title="">
&nbsp;&nbsp;${texts.gk_t}:<input type=checkbox ${add_checked(options.isShowSmithTimer)} id=isShowSmithTimerCheckbox title="">
&nbsp;&nbsp;${texts.gn_t}:<input type=checkbox ${add_checked(options.isShowMercTimer)} id=isShowMercTimerCheckbox title="">
&nbsp;&nbsp;${texts.go_t}:<input type=checkbox ${add_checked(options.isShowHuntTimer)} id=isShowHuntTimerCheckbox title="">
&nbsp;&nbsp;${texts.gv_t} (${texts.gre_t}):<input type=checkbox ${add_checked(options.isShowThiefTimer)} id=isShowThiefTimerCheckbox title="">
    </td>
</tr>
<tr>
    <td>${texts.st_gre_check}: <input type=checkbox ${add_checked(options.gre_check)} id=gre_check_id title=""></td>
</tr>
<tr>
    <td>${texts.st_go_timer_hide} "<i>${texts.regexp_go_timer} ..</i>": <input type=checkbox id=go_timer_hide_id title=""></td>
</tr>
<tr>
    <td>${texts.st_work_trudogolik_off}: <input type=checkbox ${add_checked(options.time_work_trudogolik_off)} id=trudogolik_off_id title=""></td>
</tr>
<tr>
    <td>${texts.st_work_trudogolik_show}: <input type=checkbox ${add_checked(options.time_work_trudogolik_show)} id=trudogolik_show_id title=""></td>
</tr>
<tr>
    <td>${texts.st_disable_multiple_alarms}: <input type=checkbox ${add_checked(options.disable_multiple_alarms)} id=disable_multiple_alarms_id title=""></td>
</tr>
<tr>
    <td>${texts.st_predupr_pa}</td>
</tr>
<tr>
    <td>${texts.st_predupr_go_lic}</td>
</tr>
<tr>
    <td>${texts.st_percent_faster} <input id="gv_n_percent" value="${(100 - options.time_percent_faster * 100)}" style="width: 25px;" maxlength="2"> <b>%</b> <input type="submit" id="gv_n_percent_ok" value="ok"></td>
</tr>
<tr>
    <td>${texts.st_gv_n_time} <input id="gv_n_time" type="number" value="${(60 * options.time_percent_faster * options.time_percent_prem)}" style="width: 25px;" maxlength="2"> <b>min</b>
    <input type="submit" id="gv_n_time_ok" value="ok"></td>
</tr>
<tr>
    <td> <input type="submit" id="null_tr_id" value="${texts.st_null_timers}">&nbsp;&nbsp;&nbsp;</td>
</tr>
<tr>
    <td>${texts.alarm_mode} <input type="radio" name="r_notify_type" id="r_notify_0">${texts.alarm_mode_sound}
    <input type="radio" name="r_notify_type" id="r_notify_1">${texts.alarm_mode_alert}
    <input type="radio" name="r_notify_type" id="r_notify_2">${texts.alarm_mode_both}
    <input type="radio" name="r_notify_type" id="r_notify_3">${texts.alarm_mode_none}
    </td>
</tr>
<tr>
    <td>
        <table>
            <tr>
                <td>${texts.audio_file + texts.gr_t}</td>
                <td><input size=55 type="text" id="audio_file_gr" value="${options.audio_file_gr || ''}"></td>
                <td><input size=55 type="button" id="play_audio_gr" value="Play!">  </td>
            </tr>
            <tr>
                <td>${texts.audio_file + texts.gonv_t}</td>
                <td><input size=55 type="text" id="audio_file_gonv" value="${options.audio_file_gonv || ''}"></td>
                <td><input size=55 type="button" id="play_audio_gonv" value="Play!">  </td>
            </tr>
            <tr>
                <td>${texts.audio_file + texts.gk_t}</td>
                <td><input size=55 type="text" id="audio_file_gk" value="${options.audio_file_gk || ''}"></td>
                <td><input size=55 type="button" id="play_audio_gk" value="Play!">  </td>
            </tr>
            <tr>
                <td>${texts.audio_file + texts.h_t}</td>
                <td><input size=55 type="text" id="audio_file_h" value="${options.audio_file_h || ''}"></td>
                <td><input size=55 type="button" id="play_audio_h" value="Play!">  </td>
            </tr>
        </table>
    </td>
</tr>
`;
    const optionsContainer = createElement("table", { innerHTML: bgcinnerHTML });

    optionsContainer.querySelector("#null_tr_id").addEventListener("click", null_tr);
    optionsContainer.querySelector("#gv_n_time_ok").addEventListener("click", function() { if(Number(document.getElementById("gv_n_time").value) >= 0) { GM_setValue(`AmbushTimeout${PlayerId}`, Date.now() + document.getElementById("gv_n_time").value * 60000); } });
    optionsContainer.querySelector("#gv_n_percent_ok").addEventListener("click", gv_n_percent_f);
    optionsContainer.querySelector("#gre_check_id").addEventListener("click", function() { updateOption("gre_check", this.checked ? '1' : '0'); });
    optionsContainer.querySelector("#trudogolik_show_id").addEventListener("click", function() { updateOption("time_work_trudogolik_show", this.checked ? '1' : '0'); });
    optionsContainer.querySelector("#trudogolik_off_id").addEventListener("click", function() { updateOption("time_work_trudogolik_off", this.checked ? '1' : '0'); });
    optionsContainer.querySelector("#disable_multiple_alarms_id").addEventListener("click", function() { updateOption("disable_multiple_alarms", this.checked ? '1' : '0'); });
    
    const go_timer_hide_id = optionsContainer.querySelector("#go_timer_hide_id");
    go_timer_hide_id.checked = gmGetBool("HideHuntTimer");
    go_timer_hide_id.addEventListener("click", function() { GM_setValue("HideHuntTimer", this.checked); }, false);
    
    optionsContainer.querySelector("#isShowWorkTimerCheckbox").addEventListener("click", function() { updateOption("isShowWorkTimer", this.checked ? '1' : '0'); bindTimersPanel(); });
    optionsContainer.querySelector("#isShowSmithTimerCheckbox").addEventListener("click", function() { updateOption("isShowSmithTimer", this.checked ? '1' : '0'); bindTimersPanel(); });
    optionsContainer.querySelector("#isShowMercTimerCheckbox").addEventListener("click", function() { updateOption("isShowMercTimer", this.checked ? '1' : '0'); bindTimersPanel(); });
    optionsContainer.querySelector("#isShowHuntTimerCheckbox").addEventListener("click", function() { updateOption("isShowHuntTimer", this.checked ? '1' : '0'); bindTimersPanel(); });
    optionsContainer.querySelector("#isShowThiefTimerCheckbox").addEventListener("click", function() { updateOption("isShowThiefTimer", this.checked ? '1' : '0'); bindTimersPanel(); });

    optionsContainer.querySelector("#audio_file_gr").addEventListener("change", function() { updateOption("audio_file_gr", this.value.trim()); audio_gr = initAudio(options.audio_file_gr); });
    optionsContainer.querySelector("#audio_file_gonv").addEventListener("change", function() { updateOption("audio_file_gonv", this.value.trim()); audio_gonv = initAudio(options.audio_file_gonv); });
    optionsContainer.querySelector("#audio_file_gk").addEventListener("change", function() { updateOption("audio_file_gk", this.value.trim()); audio_gk = initAudio(options.audio_file_gk); });
    optionsContainer.querySelector("#audio_file_h").addEventListener("change", function() { updateOption("audio_file_h", this.value.trim()); audio_h = initAudio(options.audio_file_h); });

    optionsContainer.querySelector("#play_audio_gr").addEventListener("click", function() { play_audio(options.audio_file_gr); });
    optionsContainer.querySelector("#play_audio_gonv").addEventListener("click", function() { play_audio(options.audio_file_gonv); });
    optionsContainer.querySelector("#play_audio_gk").addEventListener("click", function() { play_audio(options.audio_file_gk); });
    optionsContainer.querySelector("#play_audio_h").addEventListener("click", function() { play_audio(options.audio_file_h); });
    
    optionsContainer.querySelector("#r_notify_0").addEventListener("click", function() { if(this.checked) { GM_setValue(`NotificationType${PlayerId}`, "0"); } });
    optionsContainer.querySelector("#r_notify_1").addEventListener("click", function() { if(this.checked) { GM_setValue(`NotificationType${PlayerId}`, "1"); } });
    optionsContainer.querySelector("#r_notify_2").addEventListener("click", function() { if(this.checked) { GM_setValue(`NotificationType${PlayerId}`, "2"); } });
    optionsContainer.querySelector("#r_notify_3").addEventListener("click", function() { if(this.checked) { GM_setValue(`NotificationType${PlayerId}`, "3"); } });
    optionsContainer.querySelector(`#r_notify_${GM_getValue(`NotificationType${PlayerId}`, 0)}`).checked = true;
    
    createPupupPanel(GM_info.script.name, `${isEn ? "Options" : "Настройки"} ${GM_info.script.name}`, [[optionsContainer]]);
}
function add_checked(val) { return val == '1' ? 'checked' : ''; }
function play_audio(src) {
    if(src && src != '') {
        var paudio = new Audio();
        paudio.preload = 'auto';
        paudio.src = src;
        paudio.play();
    } else {
        audio_default.play();
    }
}
function gv_n_percent_f() {
    if(Number(document.getElementById("gv_n_percent").value) >= 0) {
        options = JSON.parse(GM_getValue(`hwmTimersOptions${PlayerId}`));
        options.time_percent_faster = '' + ((100 - document.getElementById("gv_n_percent").value) / 100);
        document.getElementById("gv_n_time").value = (60 * options.time_percent_faster * options.time_percent_prem);
        GM_setValue(`hwmTimersOptions${PlayerId}`, JSON.stringify(options));
    }
}
function null_tr() { // обнуление всех таймеров
    GM_deleteValue(`WorkEnd${PlayerId}`);
    GM_deleteValue(`RepairEnd${PlayerId}`);
    GM_deleteValue(`MercenaryTimeout${PlayerId}`);
    GM_deleteValue(`HuntTimeout${PlayerId}`);
    GM_deleteValue(`AmbushTimeout${PlayerId}`);
}
function getUrlParamValue(url, paramName) { return (new URLSearchParams(url.split("?")[1])).get(paramName); }
function addElement(type, parent, data) {
    let el = createElement(type, data);
    if(parent) {
        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 GM_addStyle(css) { addElement("style", document.head, { type: "text/css", innerHTML: css }); }
function getParent(element, parentType, number = 1) {
    if(!element) {
        return;
    }
    let result = element;
    let foundNumber = 0;
    while(result = result.parentNode) {
        if(result.nodeName.toLowerCase() == parentType.toLowerCase()) {
            foundNumber++;
            if(foundNumber == number) {
                return result;
            }
        }
    }
}
function getServerTime() { return Date.now() + parseInt(GM_getValue("ClientServerTimeDifference", 0)); }
function getServerDateTime() { return new Date(getServerTime()); }
function toServerTime(clientTime) { return clientTime -  parseInt(GM_getValue("ClientServerTimeDifference", 0)); }
function serverNow() { return toServerTime(Date.now()); }
function requestServerTime() {
    if(parseInt(GM_getValue("LastClientServerTimeDifferenceRequestDate", 0)) + 60 * 60 * 1000 < (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("ClientServerTimeDifference", new Date().getTime() - parseInt(responseParcing[1]) * 1000);
                }
            }
        }
        objXMLHttpReqTime.send(null);
    } else {
        setTimeout(function() { requestServerTime(); }, 60 * 60 * 1000);
    }
}
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;
}
function observe(target, handler, once = false) {
    const config = { childList: true, subtree: true };
    const ob = new MutationObserver(async function(mut, observer) {
        observer.disconnect();
        if(handler.constructor.name === 'AsyncFunction') {
            await handler();
        } else {
            handler();
        }
        if(!once) {
            observer.observe(target, config);
        }
    });
    ob.observe(target, config);
}
function createPupupPanel(panelName, panelTitle, fieldsMap, panelToggleHandler) {
    const backgroundPopupPanel = addElement("div", document.body, { id: panelName + "1", style: "position: fixed; left: 0pt; width: 100%; background: none repeat scroll 0% 0% gray; opacity: 0.5; top: 0px; height: 100%; display: block; z-index: 200;" });
    backgroundPopupPanel.addEventListener("click", function() { hidePupupPanel(panelName, panelToggleHandler); });

    const popupPanel = addElement("div", document.body, { id: panelName + "2", style: `position: fixed; width: 650px; background: none repeat scroll 0% 0%; background-image: linear-gradient(to right, #eea2a2 0%, #bbc1bf 19%, #57c6e1 42%, #b49fda 79%, #7ac5d8 100%); left: ${((document.body.offsetWidth - 650) / 2)}px; top: 150px; display: block; z-index: 200; border: 4mm ridge rgba(211, 220, 50, .6);` });
    const contentDiv = addElement("div", popupPanel, { id: panelName + "3", style: "border: 1px solid #abc; padding: 5px; margin: 2px; display: flex; flex-wrap: wrap;" });

    if(panelTitle) {
        addElement("b", contentDiv, { innerText: panelTitle, style: "text-align: center; margin: auto; width: 90%; display: block;" });
    }
    const divClose = addElement("div", contentDiv, { id: panelName + "close", title: "Close", innerText: "x", style: "border: 1px solid #abc; width: 15px; height: 15px; text-align: center; cursor: pointer;" });
    divClose.addEventListener("click", function() { hidePupupPanel(panelName, panelToggleHandler); });

    addElement("div", contentDiv, { style: "flex-basis: 100%; height: 0;"});

    if(fieldsMap) {
        let contentTable = addElement("table", contentDiv);
        for(const rowData of fieldsMap) {
            if(rowData.length == 0) { // Спомощью передачи пустой стороки-массива, указываем, что надо начать новую таблицу после брейка
                addElement("div", contentDiv, { style: "flex-basis: 100%; height: 0;"});
                contentTable = addElement("table", contentDiv);
                continue;
            }
            const row = addElement("tr", contentTable);
            for(const cellData of rowData) {
                const cell = addElement("td", row);
                if(cellData) {
                    if(typeof(cellData) == "string") {
                        cell.innerText = cellData;
                    } else {
                        cell.appendChild(cellData);
                    }
                }
            }
        }
    }
    if(panelToggleHandler) {
        panelToggleHandler(true);
    }
    return contentDiv;
}
function showPupupPanel(panelName, panelToggleHandler) {
    let backgroundPopupPanel = document.getElementById(panelName + "1");
    let popupPanel = document.getElementById(panelName + "2");
    if(backgroundPopupPanel) {
        backgroundPopupPanel.style.display = popupPanel.style.display = 'block';
        if(panelToggleHandler) {
            panelToggleHandler(true);
        }
        return true;
    }
    return false;
}
function hidePupupPanel(panelName, panelToggleHandler) {
    let backgroundPopupPanel = document.getElementById(panelName + "1");
    let popupPanel = document.getElementById(panelName + "2");
    backgroundPopupPanel.style.display = popupPanel.style.display = 'none';
    if(panelToggleHandler) {
        panelToggleHandler(false);
    }
}
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${PlayerId}`, Date.now() + restSeconds * 1000);
        } else {
            GM_deleteValue(`HealthRestoreTime${PlayerId}`);
        }
        return [heart, maxHeart, timeHeart];
    }
}
async function initUserName() {
    if(GM_getValue("TransporterUserName")) {
        GM_deleteValue("UserName");
        GM_deleteValue("TransporterUserName");
    }
    if(location.pathname == "/pl_info.php" && getUrlParamValue(location.href, "id") == PlayerId) {
        //console.log(document.querySelector("h1").innerText)
        GM_setValue("UserName", document.querySelector("h1").innerText);
    }
    if(location.pathname == "/home.php") {
        //console.log(document.querySelector(`a[href='pl_info.php?id=${PlayerId}'] > b`).innerText)
        GM_setValue("UserName", document.querySelector(`a[href='pl_info.php?id=${PlayerId}'] > b`).innerText);
    }
    if(!GM_getValue("UserName")) {
        const doc = await getRequest(`/pl_info.php?id=${PlayerId}`);
        GM_setValue("UserName", doc.querySelector("h1").innerText);
    }
}
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 parseBattleResultPanel() {
    if(GM_getValue("ChallengeState") == ChallengeState.Battle && finalResultDiv.innerHTML.length > 10) {
        GM_deleteValue("ChallengeState");
        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}, UserName: ${GM_getValue("UserName")}`);
                if(bold.parentNode.nextSibling.nextSibling.firstChild.innerText == GM_getValue("UserName")) {
                    result = "win";
                }
                break;
            }
        }
        if(result == "fail") {
            GM_setValue("AmbushSuspendExpireDate", Date.now() + AmbushMinutesInterval * 60 * 1000);
        }
        //console.log(`result: ${result}, AmbushSuspendExpireDate: ${GM_getValue("AmbushSuspendExpireDate")}`);
    }
}