您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Добавляет в инфу персов и на домашнюю: ХП армии, очки и загрузку навыков, баланс рулетки и таверны, сумму умений, перекач, день рождения персонажа
当前为
// ==UserScript== // @name hwmAdvancedPlayerInfo // @author alex_kocharin 2008-2012, Demin 2013-2015, Tamozhnya1 2024 // @namespace Tamozhnya1 // @version 17.5 // @description Добавляет в инфу персов и на домашнюю: ХП армии, очки и загрузку навыков, баланс рулетки и таверны, сумму умений, перекач, день рождения персонажа // @include /^https{0,1}:\/\/(www|my)\.(heroeswm|lordswm)\.(ru|com)\/(home|pl_info)\.php/ // @grant GM_deleteValue // @grant GM_getValue // @grant GM_setValue // @grant GM.xmlHttpRequest // @license MIT // ==/UserScript== if(typeof GM_deleteValue != 'function') { 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) { var PlayerId = playerIdMatch[1]; } const windowObject = window.wrappedJSObject || unsafeWindow; const lang = document.documentElement.lang || (location.hostname == "www.lordswm.com" ? "en" : "ru"); const isEn = lang == "en"; let playerFraction; if(location.pathname == '/home.php') { // for new home page let currentFractionIconContainer = document.querySelector("div.home_css_pl_fract.show_hint"); if(!currentFractionIconContainer) { currentFractionIconContainer = document.querySelector("a[href^='castle.php?change_faction_dialog']"); } if(currentFractionIconContainer) { const currentFractionIconImg = currentFractionIconContainer.querySelector("img"); playerFraction = currentFractionIconImg.src.split("i/f/r")[1].split(".png")[0]; } } else { const playerFractionExec = /\/i\/f\/r(\d+)\.png/.exec(document.querySelector("body").innerHTML); //console.log(playerFractionExec); if(!playerFractionExec) { console.log("Unknown player fraction"); return; }; playerFraction = playerFractionExec[1]; //console.log(playerFraction); } const isNewPersonPage = document.querySelector("div#hwm_no_zoom") ? true : false; let playerLevel; if(isNewPersonPage) { const levelInfoCell = Array.from(document.querySelectorAll("div.home_pers_info")).find(x => x.innerHTML.includes(isEn ? "Combat level" : "Боевой уровень")); playerLevel = parseInt(levelInfoCell.querySelector("div[id=bartext] > span").innerText); } else { playerLevel = parseInt(new RegExp(`<b>${isEn ? "Combat level" : "Боевой уровень"}: (\\d+?)<\\/b>`).exec(document.documentElement.innerHTML)[1]) || 0; } const unitsHealth = { peasant: [1, 4], conscript: [1, 6], archer: [1, 7], marksman: [1, 10], footman: [1, 16], squire: [1, 26], griffon: [1, 30], impergriffin: [1, 35], priest: [1, 54], inquisitor: [1, 80], cavalier: [1, 90], paladin: [1, 100], angel: [1, 180], archangel: [1, 220], brute: [101, 8], crossman: [101, 8], vindicator: [101, 23], battlegriffon: [101, 52], zealot: [101, 80], champion: [101, 100], seraph2: [101, 220], skeleton: [2, 4], skeletonarcher: [2, 4], zombie: [2, 17], plaguezombie: [2, 17], ghost: [2, 8], spectre: [2, 19], vampire: [2, 30], vampirelord: [2, 35], lich: [2, 50], archlich: [2, 55], wight: [2, 95], wraith: [2, 100], bonedragon: [2, 150], spectraldragon: [2, 160], sceletonwar: [102, 5], rotzombie: [102, 23], poltergeist: [102, 20], vampireprince: [102, 40], masterlich: [102, 55], banshee: [102, 110], ghostdragon: [102, 150], gremlin: [3, 5], mastergremlin: [3, 6], stone_gargoyle: [3, 15], obsgargoyle: [3, 20], iron_golem: [3, 18], steelgolem: [3, 24], mage: [3, 18], archmage: [3, 30], djinn: [3, 40], djinn_sultan: [3, 45], rakshasa_rani: [3, 120], rakshasa_raja: [3, 140], colossus: [3, 175], titan: [3, 190], saboteurgremlin: [103, 6], elgargoly: [103, 16], magneticgolem: [103, 28], battlemage: [103, 29], djinn_vizier: [103, 50], rakshasa_kshatra: [103, 135], stormtitan: [103, 190], pixel: [4, 5], sprite: [4, 6], dancer: [4, 12], wardancer: [4, 12], elf: [4, 10], masterhunter: [4, 14], druid: [4, 34], druideld: [4, 38], unicorn: [4, 57], silverunicorn: [4, 77], treant: [4, 175], ancienent: [4, 181], greendragon: [4, 200], emeralddragon: [4, 200], dryad_: [104, 6], winddancer: [104, 14], arcaneelf: [104, 12], ddhigh: [104, 34], pristineunicorn: [104, 80], savageent: [104, 175], crystaldragon: [104, 200], goblin: [5, 3], hobgoblin: [5, 4], wolfrider: [5, 10], wolfraider: [5, 12], orc: [5, 12], orcchief: [5, 18], ogre: [5, 50], ogremagi: [5, 65], rocbird: [5, 55], thunderbird: [5, 65], cyclop: [5, 85], cyclopking: [5, 95], behemoth: [5, 210], ancientbehemoth: [5, 250], goblinarcher: [105, 3], boarrider: [105, 14], orcrubak: [105, 20], ogrebrutal: [105, 70], firebird: [105, 65], cyclopod: [105, 100], dbehemoth: [105, 280], goblinmag: [205, 3], orcshaman: [205, 13], darkbird: [205, 60], hyenarider: [205, 13], ogreshaman: [205, 55], shamancyclop: [205, 105], cursedbehemoth: [205, 250], scout: [6, 10], assassin: [6, 14], stalker: [6, 15], maiden: [6, 16], fury: [6, 16], bloodsister: [6, 24], minotaur: [6, 31], minotaurguard: [6, 35], taskmaster: [6, 40], darkrider: [6, 40], grimrider: [6, 50], briskrider: [6, 50], hydra: [6, 80], deephydra: [6, 125], foulhydra: [6, 125], witch: [6, 80], shadow_witch: [6, 90], mistress: [6, 100], shadowdragon: [6, 200], blackdragon: [6, 240], reddragon: [6, 235], imp: [7, 4], familiar: [7, 6], horneddemon: [7, 13], hornedoverseer: [7, 13], hellhound: [7, 15], cerberus: [7, 15], succubus: [7, 20], succubusmis: [7, 30], hellcharger: [7, 50], stallion: [7, 66], pitfiend: [7, 110], pitlord: [7, 120], devil: [7, 166], archdevil: [7, 199], vermin: [107, 6], jdemon: [107, 13], hotdog: [107, 15], seducer: [107, 26], hellkon: [107, 66], pity: [107, 140], archdemon: [107, 211], defender: [8, 7], shieldguard: [8, 12], spearwielder: [8, 10], skirmesher: [8, 12], bearrider: [8, 25], blackbearrider: [8, 30], brawler: [8, 20], berserker: [8, 25], runepriest: [8, 60], runepatriarch: [8, 70], thane: [8, 100], thunderlord: [8, 120], firedragon: [8, 230], magmadragon: [8, 280], mountaingr: [108, 12], harpooner: [108, 10], whitebearrider: [108, 30], battlerager: [108, 30], runekeeper: [108, 65], flamelord: [108, 120], lavadragon: [108, 275], goblinus: [9, 3], trapper: [9, 4], fcentaur: [9, 6], ncentaur: [9, 9], warrior: [9, 12], mauler: [9, 12], shamaness: [9, 30], sdaughter: [9, 35], slayer: [9, 34], executioner: [9, 40], wyvern: [9, 90], foulwyvern: [9, 105], cyclopus: [9, 220], untamedcyc: [9, 225], goblinshaman: [109, 5], mcentaur: [109, 10], warmong: [109, 20], eadaughter: [109, 35], chieftain: [109, 48], poukai: [109, 120], bloodeyecyc: [109, 235], scorp: [10, 4], scorpup: [10, 5], duneraider: [10, 12], duneraiderup: [10, 12], shakal: [10, 24], shakalup: [10, 30], dromad: [10, 40], dromadup: [10, 45], priestmoon: [10, 50], priestsun: [10, 55], slon: [10, 100], slonup: [10, 110], anubis: [10, 160], anubisup: [10, 200] }; const perkBranchCosts = { 1: { knight_mark: 5, attack: 9, defense: 8, luck: 10, leadership: 7, dark: 8, light: 7 }, 101: { benediction: 7, defense: 10, leadership: 10, enlightenment: 8, light: 6, summon: 10, sorcery: 7 }, 2: { necr_soul: 5, defense: 11, enlightenment: 9, dark: 7, summon: 8, sorcery: 8 }, 102: { powerraise: 6, attack: 11, luck: 12, enlightenment: 8, summon: 6, sorcery: 7 }, 3: { magic_mirror: 7, enlightenment: 9, light: 10, summon: 8, destructive: 10, sorcery: 7 }, 103: { nomagicdamage: 7, attack: 8, luck: 10, leadership: 9, enlightenment: 8, destructive: 9, sorcery: 8 }, 4: { elf_shot: 7, attack: 10, defense: 9, luck: 7, leadership: 10, enlightenment: 10, light: 7, summon: 9 }, 104: { zakarrow: 4, attack: 9, defense: 8, luck: 10, leadership: 8, enlightenment: 12 }, 5: { barb_skill: 7, attack: 7, defense: 9, luck: 9, leadership: 10 }, 105: { save_rage: 6, attack: 7, defense: 8, luck: 7, leadership: 9 }, 205: { dark_blood: 5, defense: 9, luck: 10, leadership: 10, enlightenment: 8, dark: 6, sorcery: 7 }, 6: { dark_power: 7, attack: 8, luck: 9, leadership: 10, enlightenment: 10, dark: 8, destructive: 9, sorcery: 7 }, 106: { cre_master: 5, attack: 8, defense: 11, leadership: 8, luck: 10, enlightenment: 8, dark: 8 }, 7: { hellfire: 6, attack: 7, defense: 9, luck: 10, dark: 8, destructive: 10, sorcery: 8 }, 107: { consumecorpse: 5, defense: 9, enlightenment: 9, dark: 6, sorcery: 7 }, 8: { runeadv: 7, defense: 9, destructive: 11, light: 7, leadership: 9, luck: 10 }, 108: { firelord: 5, attack: 7, defense: 10, leadership: 8, luck: 8, destructive: 6, }, 9: { memoryblood: 6, attack: 7, enlightenment: 9, leadership: 9, luck: 10, defense: 9 }, 10: { dayandnight: 7, attack: 8, defense: 10, leadership: 10, light: 6, dark: 6, sorcery: 9, enlightenment: 9 } }; addStyle(` .bar_wrap { width: 120px; /*margin: 3px 0 3px 9px;*/ border: 1px solid #1C1C1C; background-color: #8C7526; box-shadow: 0 0 1px #666, inset 0 1px 1px #222; background-image: linear-gradient(#65541B, #8C7526 50%, #65541B); display: inline-block; } .bar { height: 5px; background-color: #f9e37e; border-right: 1px solid #282828; box-shadow: inset 0 0 1px #ddd; background-image: linear-gradient(#e7ae6b, #be8d55 50%, #a57b4b 51%, #ae804c); transition: all 1s ease; max-width: 150px; } .bar:hover { animation: animate-stripes 3s linear infinite; } @keyframes animate-stripes { 0% {background-position: 0 0;} 100% {background-position: 0 22px;} } .htooltip, .htooltip: visited, .tooltip: active { color: #0077AA; text-decoration: none; } .htooltip:hover { color: #0099CC; } .htooltip > span { background-color: rgba(0,0,0, 0.8); border-radius: 5px 5px 0px 0px; box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.5); color: #fff; margin-left: -1px; margin-top: -24px; opacity: 0; padding: 2px 5px; position: absolute; text-decoration: none; visibility: hidden; z-index: 10; transition: opacity 0.4s ease-in-out, visibility 0.4s ease-in-out; } .htooltip:hover > span { position: absolute; opacity: 1; visibility: visible; } .htooltip1 > span:first-child { background-color: rgba(0,0,0, 0.8); border-radius: 5px 5px 0px 0px; box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.5); color: #fff; margin-left: -1px; margin-top: -24px; opacity: 0; padding: 2px 5px; position: absolute; text-decoration: none; visibility: hidden; z-index: 10; transition: opacity 0.4s ease-in-out, visibility 0.4s ease-in-out; } .htooltip1:hover > span:first-child { position: absolute; opacity: 1; visibility: visible; } `); var lang_en = { 'Knight' : 'Knight', 'Necromancer' : 'Necromancer', 'Wizard' : 'Wizard', 'Elf' : 'Elf', 'Barbarian' : 'Barbarian', 'Dark elf' : 'Dark elf', 'Demon' : 'Demon', 'Dwarf' : 'Dwarf', 'Steepe barbarian' : 'Steepe barbarian', 'Pharaoh' : 'Pharaoh', 'Combat level' : 'Combat level', 'Hunters\' guild' : 'Hunters\' guild', 'Laborers\' guild' : 'Laborers\' guild', 'Gamblers\' guild' : 'Gamblers\' guild', 'Thieves\' guild' : 'Thieves\' guild', 'Rangers\' guild' : 'Rangers\' guild', 'Mercenaries\' guild' : 'Mercenaries\' guild', 'Commanders\' guild' : 'Commanders\' guild', 'Watchers\' guild' : 'Watchers\' guild', 'Adventurers\' guild' : 'Adventurers\' guild', 'Leaders\' Guild' : 'Leaders\' Guild', 'Smiths\' guild' : 'Smiths\' guild', 'Enchanters\' guild' : 'Enchanters\' guild', 'Enchanters' : 'Enchanters' }; var lang_ru = { 'Knight' : 'Рыцарь', 'Necromancer' : 'Некромант', 'Wizard' : 'Маг', 'Elf' : 'Эльф', 'Barbarian' : 'Варвар', 'Dark elf' : 'Темный эльф', 'Demon' : 'Демон', 'Dwarf' : 'Гном', 'Steepe barbarian' : 'Степной варвар', 'Pharaoh' : 'Фараон', 'Combat level' : 'Боевой уровень', 'Hunters\' guild' : 'Гильдия Охотников', 'Laborers\' guild' : 'Гильдия Рабочих', 'Gamblers\' guild' : 'Гильдия Картежников', 'Thieves\' guild' : 'Гильдия Воров', 'Rangers\' guild' : 'Гильдия Рейнджеров', 'Mercenaries\' guild' : 'Гильдия Наемников', 'Commanders\' guild' : 'Гильдия Тактиков', 'Watchers\' guild' : 'Гильдия Стражей', 'Adventurers\' guild' : 'Гильдия Искателей', 'Leaders\' Guild' : 'Гильдия Лидеров', 'Smiths\' guild' : 'Гильдия Кузнецов', 'Enchanters\' guild' : 'Гильдия Оружейников', 'Enchanters' : 'Оружейников' }; const text = isEn ? lang_en : lang_ru; const fractions = isEn ? ["Knight", "Necromancer", "Wizard", "Elf", "Barbarian", "Dark elf", "Demon", "Dwarf", "Tribal", "Pharaoh"] : ["Рыцарь", "Некромант", "Маг", "Эльф", "Варвар", "Темный эльф", "Демон", "Гном", "Степной варвар", "Фараон"]; main(); async function main() { tavernAndRouletteBalances(); birthday(); await getTalentsToHome(); if(location.pathname == "/pl_info.php") { talentsStatistics(); } calcArmyHealth(); // После загрузки навыков, если она есть, чтоб обработать навык "vitality" showExpBar(); setTimeout(function() { const talentsChanger = document.getElementById("hwmSetsMasterSkillSetSelectedValue"); const armyChanger = document.getElementById("hwmSetsMasterArmySetSelectedValue"); if(talentsChanger) { if(location.pathname == "/pl_info.php" && getUrlParamValue(location.href, "id") == PlayerId) { observe([talentsChanger], talentsStatistics); } if(location.pathname == "/home.php" || location.pathname == "/pl_info.php" && getUrlParamValue(location.href, "id") == PlayerId) { const panels = [armyChanger]; if(location.pathname == "/pl_info.php" && getUrlParamValue(location.href, "id") == PlayerId) { panels.push(talentsChanger); } observe(panels, calcArmyHealth); } } }, 1500); // Здесь ждем загрузки hwmSetsMaster, чтоб подписаться на элемент hwmSetsMasterSkillSetSelectedValue и hwmSetsMasterArmySetSelectedValue } async function getTalentsToHome() { if(playerLevel < 5) { return; } if(location.pathname == '/home.php') { let hwmAdvancedPlayerInfoTalentsContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsContainer"); if(!hwmAdvancedPlayerInfoTalentsContainer) { let homePageTalentsContainer; if(isNewPersonPage) { homePageTalentsContainer = document.querySelector("div.home_friends_block"); } else { homePageTalentsContainer = document.querySelector("body > center > table:nth-child(2) > tbody > tr:nth-child(1) > td:nth-child(1) > table > tbody > tr:nth-child(2) > td:nth-child(1) > table > tbody > tr > td > table > tbody > tr > td"); } addElement("br", homePageTalentsContainer); const ShowTalentsTitleSpan = addElement("span", homePageTalentsContainer, { id: "hwmAdvancedPlayerInfoTalentsCaptionContainer", innerHTML: ` » <b>${isEn ? "Talents" : "Навыки"}:</b><b id="hwmAdvancedPlayerInfoTalentsPointsContainer"></b>`, title: getShowTalentsTitleSpanTitle() }); ShowTalentsTitleSpan.addEventListener("click", function() { setValue("ShowTalents", !getBool("ShowTalents")); this.title = getShowTalentsTitleSpanTitle(); getTalentsToHomeCore(); }, false); addElement("br", homePageTalentsContainer); hwmAdvancedPlayerInfoTalentsContainer = addElement("div", homePageTalentsContainer, { id: "hwmAdvancedPlayerInfoTalentsContainer" }); } await getTalentsToHomeCore(); const talentsChanger = document.getElementById("hwmSetsMasterSkillSetSelectedValue"); if(talentsChanger) { observe([talentsChanger], getTalentsToHomeCore); //setTimeout(function() { observe([talentsChanger], getTalentsToHomeCore); }, 3000); } } } async function getTalentsToHomeCore() { if(location.pathname == '/home.php' && getBool("ShowTalents")) { const doc = await getRequest(`/pl_info.php?id=${PlayerId}`); const perksTable = getParent(doc.querySelector("a[href^='showperkinfo.php']"), "table", 2); if(perksTable) { Array.from(perksTable.querySelectorAll("a[href^='showperkinfo.php'] > img")).forEach(x => { x.style.width = `48px`; x.style.height = "auto"; }); //Array.from(perksTable.querySelectorAll("a[href^='showperkinfo.php'] > img")).forEach(x => { x.setAttribute("title", x.getAttribute("hint")); x.style.width = `48px`; x.style.height = "auto"; }); document.getElementById("hwmAdvancedPlayerInfoTalentsContainer").innerHTML = perksTable.outerHTML; if(typeof windowObject.hwm_hints_init === 'function') windowObject.hwm_hints_init(); talentsStatistics(); calcArmyHealth(); } } } function getShowTalentsTitleSpanTitle() { return isEn ? `Click for ${getBool("ShowTalents") ? "disable" : "enable"} talents loading` : `Нажмите для ${getBool("ShowTalents") ? "выключения" : "включения"} загрузки навыков`; } function talentsStatistics() { if(playerLevel < 5) { return; } let talentsContainer; let talentsCaptionContainer; let talentsPointsContainer; if(location.pathname == "/home.php") { talentsContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsContainer"); talentsCaptionContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsCaptionContainer"); talentsPointsContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsPointsContainer"); } if(location.pathname == "/pl_info.php") { talentsContainer = getParent(document.querySelector("a[href^='showperkinfo.php?name=']"), "td", 3); const layerTable = getParent(talentsContainer, "table"); talentsCaptionContainer = Array.from(layerTable.rows[0].cells).find(x => x.innerHTML.includes(isEn ? "Talents" : "Навыки")).querySelector("b"); talentsPointsContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsPointsContainer") || addElement("span", talentsCaptionContainer, { id: "hwmAdvancedPlayerInfoTalentsPointsContainer" }); } if(!talentsContainer || !talentsCaptionContainer) { return; } const talentBranchRows = talentsContainer.querySelectorAll(":scope > table > tbody > tr"); const playerTalentBranches = Array.from(talentBranchRows).map(x => Array.from(x.querySelectorAll("a[href^='showperkinfo.php?name=']")).map(y => getUrlParamValue(y.href, "name"))); //console.log(playerTalentBranches); let talentPoints = 0; const playerPerkBranchCosts = perkBranchCosts[playerFraction]; //console.log(playerPerkBranchCosts); for(const playerPerkBranche of playerTalentBranches) { let branchName = playerPerkBranche[0]; let firstPerkPromout = 1; if(["1", "2", "3"].includes(branchName.slice(-1))) { firstPerkPromout = parseInt(branchName.slice(-1)); branchName = branchName.slice(0, -1); } //console.log(`branchName: ${branchName}, branchCost: ${playerPerkBranchCosts[branchName]}, branchePerks: ${playerPerkBranche.length}, firstPerkPromout: ${firstPerkPromout}`); talentPoints += playerPerkBranchCosts[branchName] * (playerPerkBranche.length + firstPerkPromout - 1); } talentsPointsContainer.innerText = ` (${isEn ? "points" : "очки"} ${talentPoints} ${isEn ? "from" : "из"} ${10 + 5 * (playerLevel - 5)})`; } function calcArmyHealth() { let armyContainer; if(location.pathname == "/pl_info.php") { armyContainer = getParent(document.querySelector(".cre_creature72"), "center"); } if(location.pathname == "/home.php") { if(isNewPersonPage) { armyContainer = document.querySelector("div.home_css_creature_list"); } else { armyContainer = getParent(document.querySelector(".cre_creature72"), "center"); } } if(!armyContainer) { return; } const creatures = Array.from(document.querySelectorAll(`div.${isNewPersonPage ? "castle_creature54" : "cre_creature72"}`)).filter(x => x.querySelector("div#add_now_count")).map(x => ({ Name: getUrlParamValue(x.querySelector("a[href^='army_info.php?name=']").href, "name"), Amount: parseInt(x.querySelector("div#add_now_count").innerText) })); const hasVitality = document.querySelector("a[href='showperkinfo.php?name=vitality']") ? true : false; const sum = creatures.reduce((t, x) => { const army = unitsHealth[x.Name]; if(army) { t += x.Amount * (army[1] + (hasVitality ? 2 : 0)); } return t; }, 0); const unknownCreatures = creatures.filter(x => !unitsHealth[x.Name]); let unknownCreaturesMessage = ""; if(unknownCreatures.length > 0) { unknownCreaturesMessage = isEn ? `Creatures not found: ${unknownCreatures.map(x => x.Name).join(', ')}. Please, contact developer.` : `Существа не найдены: ${unknownCreatures.map(x => x.Name).join(', ')}. Пожалуйста, сообщите разработчику.`; } let hwmAdvancedPlayerInfoArmyHealthContainer = document.getElementById("hwmAdvancedPlayerInfoArmyHealthContainer"); if(!hwmAdvancedPlayerInfoArmyHealthContainer) { armyContainer.insertAdjacentHTML("afterend", ` » <b>${isEn ? "Total HP" : "Общее HP"}: </b><span id=hwmAdvancedPlayerInfoArmyHealthContainer>${sum}</span> <a id=hwmAdvancedPlayerInfouUknownCreaturesMessageContainer href="javascript:alert('${unknownCreaturesMessage}');" title="${unknownCreaturesMessage}"> (?)</a>`); hwmAdvancedPlayerInfoArmyHealthContainer = document.getElementById("hwmAdvancedPlayerInfoArmyHealthContainer"); } hwmAdvancedPlayerInfoArmyHealthContainer.innerText = sum; hwmAdvancedPlayerInfoArmyHealthContainer.style.color = hasVitality ? "#ff0000" : ""; const hwmAdvancedPlayerInfouUknownCreaturesMessageContainer = document.getElementById("hwmAdvancedPlayerInfouUknownCreaturesMessageContainer"); hwmAdvancedPlayerInfouUknownCreaturesMessageContainer.style.display = unknownCreaturesMessage ? "" : "none"; hwmAdvancedPlayerInfouUknownCreaturesMessageContainer.title = unknownCreaturesMessage; hwmAdvancedPlayerInfouUknownCreaturesMessageContainer.href = `javascript:alert('${unknownCreaturesMessage}');`; } function tavernAndRouletteBalances() { if(location.pathname != "/pl_info.php") { return; } // Баланс рулетки const rouletteDebitText = findChildrenTextContainsValue("td", isEn ? "Roulette bets total" : "Поставлено в рулетке"); const rouletteCreditText = findChildrenTextContainsValue("td", isEn ? "Roulette winnings total" : "Выиграно в рулетке"); if(rouletteDebitText.length == 1 && rouletteCreditText.length == 1 && rouletteDebitText[0].nextSibling.tagName.toLowerCase() == "b" && rouletteCreditText[0].nextSibling.tagName.toLowerCase() == "b") { const rouletteBalance = rouletteCreditText[0].nextSibling.innerText.replace(/,/g, "") - rouletteDebitText[0].nextSibling.innerText.replace(/,/g, ""); rouletteCreditText[0].nextSibling.insertAdjacentHTML("afterend", `<br> ${isEn ? 'Balance' : 'Баланс'}: <b>${rouletteBalance.toLocaleString()}</b>`); } // Баланс таверны var statisticsSecondCell = getParent(document.querySelector("td.wb > a[href^='pl_transfers.php?id']"), "td").nextSibling; var tavern_parent = statisticsSecondCell.querySelector("tr").parentNode.childNodes[0].childNodes[1].firstChild.firstChild.childNodes; const winsRow = tavern_parent[1]; const failsRow = tavern_parent[2]; tavern_parent[0].childNodes[1].setAttribute('style', 'white-space: nowrap;'); winsRow.childNodes[1].setAttribute('style', 'white-space: nowrap;'); //console.log(winsRow) failsRow.childNodes[1].setAttribute('style', 'white-space: nowrap;'); var tavern_0bal = winsRow.querySelector("tr"); var tavern_1bal = failsRow.querySelector("tr"); if(!tavern_0bal && !tavern_1bal) return; let wins = 0; let fails = 0; if(tavern_0bal) { wins = parseInt(winsRow.cells[1].querySelector("b").innerText.replace(/,/g, "")); tavern_0bal.childNodes[1].setAttribute('style', 'text-align: right; padding-right: 5px;'); tavern_0bal = tavern_0bal.childNodes[1].innerHTML.replace(/,/g, ""); winsRow.childNodes[3].firstChild.width = "100%"; var totalRow = winsRow.cloneNode(true); } else { tavern_0bal = 0; } if(tavern_1bal) { fails = parseInt(failsRow.cells[1].querySelector("b").innerText.replace(/,/g, "")); tavern_1bal.childNodes[1].setAttribute('style', 'text-align: right; padding-right: 5px;'); tavern_1bal = tavern_1bal.childNodes[1].innerHTML.replace(/,/g, ""); failsRow.childNodes[3].firstChild.width = "100%"; var totalRow = totalRow || failsRow.cloneNode(true); } else { tavern_1bal = 0; } var tavern_bal = tavern_0bal - tavern_1bal; totalRow.firstChild.innerHTML = ` ${isEn ? 'Balance:' : 'Баланс:'}`; totalRow.childNodes[1].innerHTML = `<b>${(wins - fails).toLocaleString()}</b>`; totalRow.childNodes[2].innerHTML = " "; totalRow.childNodes[3].innerHTML = ` <table border="0" cellspacing="0" cellpadding="0" width="100%"> <tbody> <tr> <td> <img class="rs" width="24" height="24" src="https://dcdn2.heroeswm.ru/i/r/48/gold.png?v=3.23de65" border="0" title="${isEn ? "Gold" : "Золото"}" alt=""> </td> <td style="text-align: right; padding-right: 5px;"> ${tavern_bal.toLocaleString()} </td> </tr> </tbody> </table>`; failsRow.insertAdjacentElement("afterend", totalRow); } function birthday() { if(location.pathname == "/pl_info.php") { const playerId = getUrlParamValue(location.href, "id"); let bifthday = GM_getValue(`PlayerBifthday${playerId}`, ""); const districtBold = Array.from(document.querySelectorAll("b")).find(x => x.innerHTML.includes(isEn ? "Location" : "Район")); districtBold.nextElementSibling.nextElementSibling.insertAdjacentHTML("afterend", ` » <b id=hwmAdvancedPlayerInfoBirthdayCaption title="${isEn ? "Click to download birthday" : "Нажмите для загрузки дня рождения"}">${isEn ? "Birthday" : "День рождения"}: </b><span id=hwmAdvancedPlayerInfoBirthday>${bifthday}</span>`); document.getElementById("hwmAdvancedPlayerInfoBirthdayCaption").addEventListener("click", loadBirthday); } } async function loadBirthday() { const playerId = getUrlParamValue(location.href, "id"); const firstProtocolPage = await getRequest(`/pl_transfers.php?id=${playerId}&page=50000`); const lines = firstProtocolPage.querySelector(`div#set_mobile_max_width > div > div`).innerHTML.split("<br>").filter(x => x != ""); bifthday = lines[lines.length - 2]; bifthday = bifthday.replace(/ /g, ""); GM_setValue(`PlayerBifthday${playerId}`, bifthday); document.getElementById("hwmAdvancedPlayerInfoBirthday").innerHTML = bifthday; } function showExpBar() { const player = { SkillLevel: [], SkillNumber: []}; let skillInfoCell; if(isNewPersonPage) { const levelInfoCell = Array.from(document.querySelectorAll("div.home_pers_info")).find(x => x.innerHTML.includes(text['Combat level'])); player.Level = parseInt(levelInfoCell.querySelector("div[id=bartext] > span").innerText); player.Expirience = parseInt(levelInfoCell.querySelector("div.home_text_exp").firstChild.textContent.replace(/,/g, "")); const homeContainerBlocks = Array.from(document.querySelectorAll("div#set_mobile_max_width > div > div.home_container_block")); //console.log(homeContainerBlocks) skillInfoCell = homeContainerBlocks.find(x => x.innerHTML.includes(text['Knight'])); Array.from(skillInfoCell.querySelectorAll("div[id=row]")).forEach((x, i) => { player.SkillLevel[i] = parseInt(x.querySelector("div#bartext span").innerText); player.SkillNumber[i] = parseFloat(x.querySelector("div.home_text_exp").firstChild.textContent); }); const guildsDiv = homeContainerBlocks.find(x => x.innerHTML.includes(text['Enchanters'])); Array.from(guildsDiv.querySelectorAll("div[id=row]")).forEach(x => { const key = findKey(text, y => text[y].includes(x.querySelector("span.home_guild_text").innerText)); if(key) { player[key] = parseInt(x.querySelector("div#bartext span").innerText); } }); } else { const levelInfoBold = Array.from(document.querySelectorAll("td > b")).find(x => x.innerHTML.includes(text['Combat level'])); const levelRegex = new RegExp(`${text['Combat level']}: (\\d{1,2})`); player.Level = parseInt(levelRegex.exec(levelInfoBold.innerHTML)[1]); const expirienceRegex = /\(([\d\.\,]+)\)/g; const expirienceRegexExec = expirienceRegex.exec(levelInfoBold.parentNode.innerHTML); if(expirienceRegexExec) { player.Expirience = parseInt(expirienceRegexExec[1].replace(/,/g, "")); } skillInfoCell = Array.from(document.querySelectorAll("td")).find(x => x.innerHTML.includes(text['Knight']) && x.innerHTML.includes(text['Enchanters\' guild']) && !x.innerHTML.includes("<td")); const regex = /\(([\d\.\,]+)\)/g; for(const fraction of fractions) { const skillRegex = new RegExp(`${fraction}: (\\d{1,2})`); const skillsData = skillRegex.exec(skillInfoCell.innerHTML); player.SkillLevel.push(parseInt(skillsData[1])); const skillsNumberData = regex.exec(skillInfoCell.innerHTML); let skillNumber = 0; if(skillsNumberData) { skillNumber = parseFloat(skillsNumberData[1]); } player.SkillNumber.push(skillNumber); } for(const key of Object.keys(text).filter(x => x.includes("guild") || x.includes("Guild"))) { const guildName = text[key]; let guildRegex = new RegExp(` ${guildName}: (\\d{1,2}) \\(`, "g"); if(key == 'Hunters\' guild') { guildRegex = new RegExp(` ${guildName}: .+>(\\d{1,2})<`, "g"); } player[key] = parseInt(guildRegex.exec(skillInfoCell.innerHTML)[1]); } } player.GuildStatsNumber = getGuildStatsNumber(player); player.MainSkillStatsNumber = getMainSkillStatsNumber(player); player.SkillSum = Math.floor(player.SkillLevel.reduce((t, x) => t + x, 0) * 0.25); player.TotalPoints = player.GuildStatsNumber + player.MainSkillStatsNumber + player.SkillSum; player.TotalSkills = round00(player.SkillNumber.reduce((t, x) => t + x, 0)); player.Ratio = player.TotalSkills > 0 ? Math.round(player.Expirience / player.TotalSkills) : 0; //console.log(player); if(player.Level > 2) { //console.log(`isNewPersonPage: ${isNewPersonPage}`) const playerPersents = getPlayerPercents(player); let resumeText = "В норме!"; let resumeTitle = ""; const prevEdgeSpan = getSpanHtml(playerPersents.prevEdge, playerPersents.prevEdgeClarification); const totalPointsSpan = getSpanHtml(player.TotalPoints, isEn ? "Player points" : "Очков игрока"); const guildStatsNumberSpan = getSpanHtml(player.GuildStatsNumber, isEn ? "total guilds parameters" : "сумма параметров от всех гильдий"); const skillSumSpan = getSpanHtml(player.SkillSum, isEn ? "total factions skill levels*0.25" : "сумма уровней умений фракций*0.25"); const mainSkillStatsNumberSpan = getSpanHtml(player.MainSkillStatsNumber, isEn ? "max faction skill level parameters (with possible faction potion)" : "сумма параметров от максимального умения фракции(с учётом возможного применения \"зелья фракции\")"); const nextEdgeSpan = getSpanHtml(playerPersents.nextEdge, playerPersents.nextEdgeClarification); const progressText = `${isEn ? "Main progress" : "Основной прогресс"}: ${playerPersents.normalPercent}%. ${prevEdgeSpan}->${totalPointsSpan}=(${guildStatsNumberSpan}+${skillSumSpan}+${mainSkillStatsNumberSpan})->${nextEdgeSpan}`; if(playerPersents.advacedPersent > 0){ resumeText = `Перекач! ${isEn ? "Exp" : "Опыт"}: +${playerPersents.advacedPersent}%`; //resumeTitle = `${isEn ? "More experience" : "Опыта больше на"}: ${playerPersents.advacedPersent}%`; } if(playerPersents.advacedPersent < 0) { resumeText = `Недокач!`; resumeTitle = `${isEn ? "more skill points will be awarded in proportion to the missing points" : "будет начисляться больше очков умений в соотношении к недостающим очкам"}`; } const resumeSpan = getSpanHtml(resumeText, resumeTitle); if(isNewPersonPage) { const ratioSpan = getSpanHtml(player.Ratio.toLocaleString(), isEn ? "Expirience points to one skill point" : "Очков опыта на одно очко умения"); skillInfoCell.querySelector("div.home_inside_margins").insertAdjacentHTML("beforeend", ` <div class="home_scroll_content" id="row" onclick="show_all_hwm_exp();"> <span class="home_guild_text">${isEn ? "Total skills" : "Сумма умений"}</span> <div class="home_text_exp" style="display: block;">${player.TotalSkills.toLocaleString()} (${ratioSpan})</div> </div> <div class="home_scroll_content htooltip1" id="row" onclick="show_all_hwm_exp();"> <span>${progressText}</span> <span class="home_guild_text">${isEn ? "Progress" : "Прогресс"} <div id="bar" class="home_bar_exp" style="opacity: 1;"><div id="barprogress" style="width: ${playerPersents.normalPercent}%;"></div></div> <div class="home_text_exp" style="display: block;">${playerPersents.normalPercent}% ${resumeSpan}</div> `); } else { const levelInfoBold = getParent(Array.from(document.querySelectorAll("td > b")).find(x => x.innerHTML.includes(isEn ? "Combat level:" : "Боевой уровень:")), "td").querySelector("br"); levelInfoBold.insertAdjacentHTML("afterend", ` <span id=hwmAdvancedPlayerInfoTotalSkillsSpan> » <b>${isEn ? "Skills count" : "Сумма умений"}: </b>${player.TotalSkills.toLocaleString()}, ${isEn ? "ratio" : "соотношение"}: <span title="${isEn ? "Expirience points to one skill point" : "Очков опыта на одно очко умения"}">${ player.Ratio.toLocaleString() }</span> </span> <br> » <b>${isEn ? "Progress" : "Прогресс"}:</b> <div class="bar_wrap htooltip"> <div class="bar" style="width: ${playerPersents.normalPercent}%"></div> <span>${progressText}</span> </div> <div style='font-size: 8px; font-weight: bold; display: inline-block;'>${playerPersents.normalPercent}% ${resumeSpan}</div>`); } } } function getSpanHtml(value, title) { return `<span title="${title}">${value}</span>`; } function getGuildStatsNumber(player) { const mercStats = [0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11]; const commandersStats = [0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; const watchersStats = [0, 1, 2, 3, 4, 5, 6, 7, 7.5, 8.5]; const leadersStats = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5, 0.5, 1]; const hunters = player['Hunters\' guild']; const laborers = Math.floor(player['Laborers\' guild'] / 2); const thieves = Math.max(player['Thieves\' guild'], player['Rangers\' guild']) / 2; const mercenaries = mercStats[player['Mercenaries\' guild']]; const commanders = commandersStats[player['Commanders\' guild']]; const watchers = watchersStats[player['Watchers\' guild']]; const adventurers = player['Adventurers\' guild']; const leaders = leadersStats[player['Leaders\' Guild']]; //console.log(`Hunters: ${hunters}, Laborers: ${laborers}, Thieves/Rangers: ${thieves}, Mercenaries: ${mercenaries}, Commanders: ${commanders}, Watchers: ${watchers}, Adventurers: ${adventurers}, Leaders: ${leaders}`); return hunters + laborers + thieves + mercenaries + commanders + watchers + adventurers + leaders; } function getMainSkillStatsNumber(player) { let skillLevel = player.SkillLevel.reduce((t, x) => Math.max(t, x), 0); const averageSkill = [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 10, 11, 12, 12, 12, 13, 13, 13, 13, 14]; skillLevel = Math.max(skillLevel, averageSkill[player.Level]); const skillStats = [0, 1, 2, 2.5, 4, 5.5, 6.5, 9, 11.5, 16, 21, 27, 33, 35, 36.5]; return skillStats[skillLevel]; } function getPlayerPercents(player) { //player.Level++; //для тестов const averagePoints = [0, 0, 0, 3, 5, 7, 10, 13, 17, 20, 25, 29, 35, 41, 48, 58, 68, 77, 84, 90, 96, 101, 108, 114, 125, 136, 147]; const penaltyProcents = [0, 25, 500, 1000]; // Ищем перекач // 101, 108, 114, 125 let advacedPersent = 0; const nextLevels = averagePoints.slice(player.Level + 1, player.Level + 5); const prevLevels = averagePoints.slice(player.Level - 4, player.Level).reverse(); //console.log(prevLevels) let beginNormalPoint = prevLevels[0]; let endNormalPoint = nextLevels[0] + 2; //console.log(`beginNormalPoint: ${beginNormalPoint}, endNormalPoint: ${endNormalPoint}, player.TotalPoints: ${player.TotalPoints}, player.Level: ${player.Level}`) let prevEdge = beginNormalPoint; let nextEdge = endNormalPoint; let prevEdgeClarification = isEn ? `Average points on ${player.Level - 1} level` : `Средние очки на ${player.Level - 1} уровне`; let nextEdgeClarification = isEn ? `Average points + 2 on ${player.Level + 1} level` : `Средние очки + 2 на ${player.Level + 1} уровне`; if(player.TotalPoints <= endNormalPoint) { if(player.TotalPoints < beginNormalPoint) { // Недокач - будет начисляться больше очков умений в соотношении к недостающим очкам } if(player.TotalPoints < beginNormalPoint) { prevLevels.forEach((x, i) => { const add = i == 0 ? 0 : 0; if(player.TotalPoints < (x - add) && player.TotalPoints >= prevLevels[i + 1]) { const beginPoint = x - add; const endPoint = prevLevels[i + 1]; const beginProcent = -penaltyProcents[i] - 1; const endProcent = penaltyProcents[i + 1]; const advacedPoints = beginPoint - player.TotalPoints; const percentStep = Math.round((endProcent - beginProcent) / (endPoint - beginPoint)); advacedPersent = beginProcent + percentStep * advacedPoints; //console.log(`excessLevel: ${i}, player.TotalPoints: ${player.TotalPoints}, levelAveragePoints: ${x}, add: ${add}, beginPoint: ${beginPoint}, endPoint: ${endPoint}, beginProcent: ${beginProcent}, endProcent: ${endProcent}, advacedPoints: ${advacedPoints}, percentStep: ${percentStep}, advacedPersent: ${advacedPersent}`); prevEdge = endPoint; nextEdge = beginPoint; prevEdgeClarification = isEn ? `Average points on ${player.Level - (i + 1)} level` : `Средние очки на ${player.Level - (i + 1)} уровне`; nextEdgeClarification = isEn ? `Average points on ${player.Level - i} level` : `Средние очки на ${player.Level - i} уровне`; } if(i == prevLevels.length - 1 && player.TotalPoints < prevLevels[i + 1]) { advacedPersent = penaltyProcents[i + 1]; //console.log(`excessLevel: ${i}, player.TotalPoints: ${player.TotalPoints}, levelAveragePoints: ${x}, add: ${add}, advacedPersent: ${advacedPersent}`); } }); } } else { nextLevels.forEach((x, i) => { const add = i == 0 ? 2 : 0; if(player.TotalPoints > (x + add) && player.TotalPoints <= nextLevels[i + 1]) { const beginPoint = x + add; const endPoint = nextLevels[i + 1]; const beginProcent = penaltyProcents[i] + 1; const endProcent = penaltyProcents[i + 1]; const advacedPoints = player.TotalPoints - beginPoint; const percentStep = Math.round((endProcent - beginProcent) / (endPoint - beginPoint)); advacedPersent = beginProcent + percentStep * advacedPoints; //console.log(`excessLevel: ${i}, player.TotalPoints: ${player.TotalPoints}, levelAveragePoints: ${x}, add: ${add}, beginPoint: ${beginPoint}, endPoint: ${endPoint}, beginProcent: ${beginProcent}, endProcent: ${endProcent}, advacedPoints: ${advacedPoints}, percentStep: ${percentStep}, advacedPersent: ${advacedPersent}`); prevEdge = x + add; nextEdge = endPoint; prevEdgeClarification = isEn ? `Average points${add > 0 ? ` + ${add}` : ""} on ${player.Level + 1 + i} level` : `Средние очки${add > 0 ? ` + ${add}` : ""} на ${player.Level + 1 + i} уровне`; nextEdgeClarification = isEn ? `Average points on ${player.Level + 1 + i + 1} level` : `Средние очки на ${player.Level + 1 + i + 1} уровне`; } if(i == nextLevels.length - 1 && player.TotalPoints > nextLevels[i + 1]) { advacedPersent = penaltyProcents[i + 1]; //console.log(`excessLevel: ${i}, player.TotalPoints: ${player.TotalPoints}, levelAveragePoints: ${x}, add: ${add}, advacedPersent: ${advacedPersent}`); } }); } return { beginNormalPoint: beginNormalPoint, endNormalPoint: endNormalPoint, normalPercent: Math.max(Math.min(Math.round((player.TotalPoints - beginNormalPoint) * 100 / (endNormalPoint - beginNormalPoint)), 100), 0), advacedPersent: advacedPersent, prevEdge: prevEdge, nextEdge: nextEdge, prevEdgeClarification: prevEdgeClarification, nextEdgeClarification: nextEdgeClarification }; } // API 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 getUrlParamValue(url, paramName) { return (new URLSearchParams(url.split("?")[1])).get(paramName); } function findChildrenTextContainsValue(selector, value) { return Array.from(document.querySelectorAll(selector)).reduce((t, x) => { const match = Array.from(x.childNodes).filter(y => y.nodeName == "#text" && y.textContent.includes(value)); return [...t, ...match]; }, []); } function getValue(key, defaultValue) { return GM_getValue(key, defaultValue); }; function setValue(key, value) { GM_setValue(key, value); }; function deleteValue(key) { return GM_deleteValue(key); }; function getPlayerValue(key, defaultValue) { return getValue(`${key}${PlayerId}`, defaultValue); }; function setPlayerValue(key, value) { setValue(`${key}${PlayerId}`, value); }; function deletePlayerValue(key) { return deleteValue(`${key}${PlayerId}`); }; function getPlayerBool(valueName, defaultValue = false) { return getBool(valueName + PlayerId, defaultValue); } function getBool(valueName, defaultValue = false) { const value = getValue(valueName); //console.log(`valueName: ${valueName}, value: ${value}, ${typeof(value)}`) if(value) { if(typeof(value) == "string") { return value == "true"; } if(typeof(value) == "boolean") { return value; } } return defaultValue; } function round00(value) { return Math.round(value * 100) / 100; } 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 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 addStyle(css) { addElement("style", document.head, { type: "text/css", innerHTML: css }); } function getRequest(url, overrideMimeType) { return new Promise((resolve, reject) => { GM.xmlHttpRequest({ method: "GET", url: url, overrideMimeType: overrideMimeType || "text/html; charset=windows-1251", onload: function(response) { resolve((new DOMParser).parseFromString(response.responseText, "text/html")); }, onerror: function(error) { reject(error); } }); }); } function observe(targets, handler, config = { childList: true, subtree: true }) { targets = Array.isArray(targets) ? targets : [targets]; targets = targets.map(x => { if(typeof x === 'function') { return x(document); } return x; }); // Можем передавать не элементы, а их селекторы const ob = new MutationObserver(async function(mut, observer) { //console.log(`Mutation start`); observer.disconnect(); if(handler.constructor.name === 'AsyncFunction') { await handler(); } else { handler(); } for(const target of targets) { if(target) { observer.observe(target, config); } } }); for(const target of targets) { if(target) { ob.observe(target, config); } } } function findKey(obj, selector) { return Object.keys(obj).find(selector); }