// ==UserScript==
// @name hwmAdvancedPlayerInfo
// @author alex_kocharin 2008-2012, Demin 2013-2015, Tamozhnya1 2024
// @namespace Tamozhnya1
// @version 20.1
// @description Добавляет в инфу персов и на домашнюю: ХП армии, очки и загрузку навыков, баланс рулетки и таверны, сумму умений, перекач, день рождения персонажа. Спойлеры. Статистика клана.
// @include /^https{0,1}:\/\/(www|my)\.(heroeswm|lordswm)\.(ru|com)\/(home|pl_info|clan_info|tournaments)\.php/
// @grant GM_deleteValue
// @grant GM_getValue
// @grant GM_setValue
// @grant GM.xmlHttpRequest
// @require https://update.greasyfork.org/scripts/490927/1360667/Tamozhnya1Lib.js
// @license MIT
// ==/UserScript==
// Включить/выключить загрузку навыков на домашней странице можно щелчком по надписи "Навыки". День рождения загружается щелчком по надписи. Стоимость боя показывается скриптом hwmOptimalRepairAtMarket. Туда же перенесены расчет ОА и крафта.
// Оригинальный скрипт https://userscripts-mirror.org/scripts/show/178809 (скрипт демина с greasyfork почему-то удален, как и таймеры)
if(!PlayerId) {
return;
}
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],
wdancer: [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],
shadow_witch: [6, 80],
matriarch: [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],
nightmare: [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: 9,
luck: 10,
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,
leadership: 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
},
109: {
absoluterage: 7,
attack: 7,
shout: 7,
leadership: 9,
luck: 10,
defense: 12
},
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
addPlayerInfoSpoilers();
clanStatistics();
if(location.pathname == "/tournaments.php") {
const currentTours = [...document.querySelectorAll("center > h2")].find(x => (x.innerText == (isEn ? "Current tournaments" : "Текущие турниры")));
const filterByLevelSpan = addElement("span", { innerHTML: ` <input id=filterByLevelCheckbox type=checkbox title="${isEn ? "Filter by level" : "Фильтровать по уровню"}">`, style: "font-size: 12px;" }, currentTours);
const filterByLevelCheckbox = filterByLevelSpan.querySelector("#filterByLevelCheckbox");
filterByLevelCheckbox.checked = getPlayerBool("filterToursByLevel");
filterByLevelCheckbox.addEventListener("change", function() { setPlayerValue("filterToursByLevel", this.checked); applyToursFilter(); });
applyToursFilter();
}
}
function applyToursFilter() {
[...document.querySelectorAll("td > b > font")].filter(x => x.closest("td").innerHTML.includes(isEn ? "Participants level" : "Уровень участников")).forEach(x => {
x.closest("table").style.display = getPlayerBool("filterToursByLevel") ? "none" : "";
});
}
async function getTalentsToHome() {
if(location.pathname == '/home.php') {
const playerLevel = getViewingPlayerLevel();
if(playerLevel < 5) {
return;
}
let hwmAdvancedPlayerInfoTalentsContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsContainer");
if(!hwmAdvancedPlayerInfoTalentsContainer) {
let homePageTalentsContainer;
if(isNewPersonPage) {
homePageTalentsContainer = document.querySelector("div.home_friends_block");
homePageTalentsContainer.insertAdjacentHTML("afterend", `<div class="home_container_block" style="align-items: left;">
<div class="global_container_block_header global_a_hover">
<span id=hwmAdvancedPlayerInfoTalentsCaptionContainer title="${getShowTalentsTitleSpanTitle()}"><b>${isEn ? "Talents" : "Навыки"}:</b><b id="hwmAdvancedPlayerInfoTalentsPointsContainer"></b></span>
</div>
<div id=hwmAdvancedPlayerInfoTalentsContainer class="home_inside_margins global_a_hover">
</div>
</div>`);
} 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");
homePageTalentsContainer.insertAdjacentHTML("beforeend", `<br />
<span id=hwmAdvancedPlayerInfoTalentsCaptionContainer title="${getShowTalentsTitleSpanTitle()}"> » <b>${isEn ? "Talents" : "Навыки"}:</b><b id="hwmAdvancedPlayerInfoTalentsPointsContainer"></b></span>
<br />
<div id=hwmAdvancedPlayerInfoTalentsContainer></div>
`);
}
document.getElementById("hwmAdvancedPlayerInfoTalentsCaptionContainer").addEventListener("click", function() { setValue("ShowTalents", !getBool("ShowTalents")); this.title = getShowTalentsTitleSpanTitle(); getTalentsToHomeCore(); }, false);
}
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 win.hwm_hints_init === 'function') win.hwm_hints_init();
talentsStatistics();
calcArmyHealth();
}
}
}
function getShowTalentsTitleSpanTitle() { return isEn ? `Click for ${getBool("ShowTalents") ? "disable" : "enable"} talents loading` : `Нажмите для ${getBool("ShowTalents") ? "выключения" : "включения"} загрузки навыков`; }
function talentsStatistics() {
const playerLevel = getViewingPlayerLevel();
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");
if(layerTable) {
talentsCaptionContainer = Array.from(layerTable.rows[0].cells).find(x => x.innerHTML.includes(isEn ? "Talents" : "Навыки")).querySelector("b");
}
talentsPointsContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsPointsContainer") || addElement("span", { id: "hwmAdvancedPlayerInfoTalentsPointsContainer" }, talentsCaptionContainer);
}
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 playerFraction = getPlayerFraction();
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" : "Район"));
if(districtBold) {
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() {
if(location.pathname != '/home.php' && location.pathname != '/pl_info.php') {
return;
}
const player = { SkillLevel: [], SkillNumber: [], Expirience: 0 };
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']) || x.innerHTML.includes('Кавалер'));
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('Кавалер')) && 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);
if(!skillsData) {
continue;
}
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.TotalSkillLevel = player.SkillLevel.reduce((t, x) => t + x, 0);
player.SkillSum = Math.floor(player.TotalSkillLevel * 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);
console.log(playerPersents);
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 (${player.TotalSkillLevel}) * 0.25` : `сумма уровней умений фракций (${player.TotalSkillLevel}) * 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", `<meter id="playerPointsMeter" min="0" max="147" low="${playerPersents.beginNormalPoint}" high="${playerPersents.endNormalPoint}" optimum="${playerPersents.averagePoint}" value="${player.TotalPoints}"></meter>`);
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, 9.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, 14, 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, 158, 169];
const penaltyProcents = [0, 25, 500, 1000, 1000, 1000, 1000, 1000, 1000, 1000];
// Ищем перекач // 101, 108, 114, 125
let advacedPersent = 0;
const hiLevel = Math.min(player.Level + 6, averagePoints.length - 1);
const nextLevels = averagePoints.slice(player.Level + 1, hiLevel);
const lowLevel = Math.max(player.Level - 4, 0);
const prevLevels = averagePoints.slice(lowLevel, player.Level).reverse();
console.log(prevLevels)
const averagePoint = averagePoints[player.Level];
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 + 2)} level` : `Средние очки на ${player.Level - (i + 2)} уровне`;
nextEdgeClarification = isEn ? `Average points on ${player.Level - (i + 1)} level` : `Средние очки на ${player.Level - (i + 1)} уровне`;
}
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 = Math.min(penaltyProcents[i] + 1, 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 {
averagePoint: averagePoint,
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
};
}
function addPlayerInfoSpoilers() {
if(location.pathname == "/pl_info.php") {
const panelNames = isEn ? ["Statistics", "Clans", "Resources", "Best stacks in the Leaders' Guild", "Achievements", "Personal info"] : ["Статистика", "Кланы", "Ресурсы", "Лучшие отряды Гильдии Лидеров", "Достижения", "Личная информация"];
const bolds = Array.from(document.querySelectorAll("td > b"));
for(const panelName of panelNames) {
const panelTitleBold = bolds.find(x => x.innerText == panelName);
if(panelTitleBold) {
const panelTitle = panelTitleBold.closest("td");
const spoiler = addElement("div", { id: getSpoilerId(panelName), style: "display: inline-block; cursor: pointer;", innerHTML: `<img src="https://dcdn.heroeswm.ru/i/inv_im/btn_expand.svg" style="vertical-align: middle;">` }, panelTitle);
spoiler.addEventListener("click", function() { setPlayerValue(this.id, !getPlayerBool(this.id)); bindPlayerInfoSpolers(this.id); });
if(panelName == (isEn ? "Statistics" : "Статистика")) {
addElement("a", { innerHTML: ` <b>${isEn ? "Battles" : "Бои"}</b>`, href: `/pl_warlog.php?id=${getUrlParamValue(location.href, "id")}` }, panelTitle);
addElement("a", { innerHTML: ` <b>${isEn ? "Transfers" : "Передачи"}</b>`, href: `/pl_transfers.php?id=${getUrlParamValue(location.href, "id")}` }, panelTitle);
}
if(panelName == (isEn ? "Clans" : "Кланы")) {
const clanRef = panelTitle.closest("tr").nextElementSibling.querySelector("a[href^='clan_info.php']");
if(clanRef) {
const clone = clanRef.cloneNode(true);
panelTitle.insertAdjacentElement("beforeend", clone);
}
}
}
}
bindPlayerInfoSpolers();
}
}
function bindPlayerInfoSpolers(spoilerId) {
const panelNames = isEn ? ["Statistics", "Clans", "Resources", "Best stacks in the Leaders' Guild", "Achievements", "Personal info"] : ["Статистика", "Кланы", "Ресурсы", "Лучшие отряды Гильдии Лидеров", "Достижения", "Личная информация"];
const spoilerIds = spoilerId ? [spoilerId] : panelNames.map(x => getSpoilerId(x));
for(const spoilerId of spoilerIds) {
const spoiler = document.getElementById(spoilerId);
if(spoiler) {
const spoiled = getPlayerBool(spoilerId);
spoiler.querySelector("img").style.transform = spoiled ? 'rotate(0deg)' : 'rotate(90deg)';
spoiler.closest("tr").nextElementSibling.style.display = spoiled ? "none" : "";
}
}
}
function getSpoilerId(panelName) { return `${panelName.replace(/\s/g, "").replace(/'/g, "")}Spoiler`; }
function clanStatistics() {
if(location.pathname == "/clan_info.php") {
const clanActivity = document.querySelector("img[src$='clans/online.gif']") || document.querySelector("img[src$='clans/offline.gif']");
if(!clanActivity) {
return;
}
const clanHeroesTable = clanActivity.closest("table");
const onlineAmount = clanHeroesTable.querySelectorAll("img[src$='clans/online.gif']").length;
const offlineAmount = clanHeroesTable.querySelectorAll("img[src$='clans/offline.gif']").length;
const inBattleAmount = clanHeroesTable.querySelectorAll("img[src$='clans/battle.gif']").length;
const playArcomagAmount = clanHeroesTable.querySelectorAll("img[src$='clans/arcomag.gif']").length;
const clanInfoTable = document.querySelector("a[href^='clan_log.php?id']").closest("table");
const firstRow = clanInfoTable.rows[0];
firstRow.cells[0].insertAdjacentHTML("beforeend", `
<span id=shortClanActivityInfoSpan>
<span style="${onlineAmount > 0 ? "" : "display: none;"}"><img src="https://dcdn.heroeswm.ru/i/clans/online.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "Online" : "В игре"}:</b> ${onlineAmount} </span>
<span style="${inBattleAmount > 0 ? "" : "display: none;"}"><img src="https://dcdn.heroeswm.ru/i/clans/battle.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "In combat" : "В бою"}:</b> ${inBattleAmount} </span>
<span style="${playArcomagAmount > 0 ? "" : "display: none;"}"><img src="https://dcdn.heroeswm.ru/i/clans/arcomag.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "In card match" : "В таверне"}:</b> ${playArcomagAmount} </span>
</span>`);
const spoiler = addElement("div", { id: `clanStatisticsSpoiler`, style: "display: inline-block; cursor: pointer;", innerHTML: `<img src="https://dcdn.heroeswm.ru/i/inv_im/btn_expand.svg" style="vertical-align: middle;">` }, document.getElementById("shortClanActivityInfoSpan"));
spoiler.addEventListener("click", function() { setPlayerValue("clanStatisticsSpoiled", !getPlayerBool("clanStatisticsSpoiled")); clanStatisticsRowBind(); });
const clanHeroes = Array.from(clanHeroesTable.rows).filter(x => x.cells[2].querySelector("a[href^='pl_info.php?id=']")).map(x => {
const level = parseInt(x.cells[3].innerText);
const fractionImage = x.cells[2].querySelector("img");
const fractionNumber = parseInt(fractionImage.src.match(/\/i\/f\/r(\d+).png/)[1]);
return { level: level, fractionImage: fractionImage, fraction: fractionImage.title, fractionNumber: fractionNumber };
});
const clanHeroesAmount = clanHeroes.length;
const heroesLevels = groupBy(clanHeroes, "level");
const clanLevels = Object.keys(heroesLevels).map(x => ({ level: parseInt(x), amount: heroesLevels[x].length }));
clanLevels.sort((a, b) => b.level - a.level);
const heroesFractions = groupBy(clanHeroes, "fractionNumber"); // console.log(heroesFractions);
const clanFractions = Object.keys(heroesFractions).map(x => ({
fractionNumber: parseInt(x) % 100,
amount: heroesFractions[x].length,
fractionImage: heroesFractions[x][0].fractionImage,
fraction: heroesFractions[x][0].fraction,
altNumber: parseInt(x) >= 100 ? Math.round(parseInt(x) / 100) : 0
}));
clanFractions.sort((a, b) => a.fractionNumber == b.fractionNumber ? a.altNumber - b.altNumber : a.fractionNumber - b.fractionNumber); // console.log(clanFractions);
const clanStatisticsRow = addElement('tr', { id: "clanStatisticsRow" }, firstRow, "afterend");
clanStatisticsRow.innerHTML = `
<td colspan="2" class="wbwhite">
<table width="100%" height="100%">
<tr>
<td width="60%" valign="top" style="border-right:1px #5D413A solid;">
<table width="100%" cellpadding="5">
<tr>
<td align="center">
<b>${isEn ? 'All' : 'Всего'}:</b> ${clanHeroesAmount} <b>${isEn ? 'Online' : 'В сети'}:</b> ${clanHeroesAmount - offlineAmount} (${Math.round((clanHeroesAmount - offlineAmount) / clanHeroesAmount * 100)}%)
</td>
</tr>
<tr>
<td align="center" style="white-space:nowrap">
<img src="https://dcdn.heroeswm.ru/i/clans/online.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "Online" : "В игре"}:</b> ${onlineAmount}
<img src="https://dcdn.heroeswm.ru/i/clans/battle.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "In combat" : "В бою"}:</b> ${inBattleAmount}
<img src="https://dcdn.heroeswm.ru/i/clans/arcomag.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "In card match" : "В таверне"}:</b> ${playArcomagAmount}
<img src="https://dcdn.heroeswm.ru/i/clans/offline.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "Offline" : "Не в игре"}:</b> ${offlineAmount}
</td>
</tr>
<tr>
<td>
${clanFractions.map(x => `<img src="${x.fractionImage.src}" align="absmiddle" border="0" height="15" width="15"> <b>${x.fraction}:</b> ${x.amount} (${Math.round(x.amount / clanHeroesAmount * 100)}%)`).join("<br>")}
</td>
</tr>
</table>
</td>
<td valign="top" height="100%">
<table width="100%" height="100%" cellpadding="5">
<tr>
<td align="center">
<b>${isEn ? 'The average level of heroes' : 'Средний уровень героев'}:</b> ${round0(clanHeroes.reduce((t, x) => t + x.level, 0) / clanHeroesAmount)}
</td>
</tr>
<tr>
<td height="100%" valign="middle">
${clanLevels.map(x => `<b>${x.level} ${isEn ? 'level' : 'уровней'}:</b> ${x.amount} (${Math.round(x.amount / clanHeroesAmount * 100)}%)`).join("<br>")}
</td>
</tr>
<tr>
<td align="right" valign="bottom">
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>`;
clanStatisticsRowBind();
}
}
function clanStatisticsRowBind() {
const spoiled = getPlayerBool("clanStatisticsSpoiled");
const clanStatisticsSpoiler = document.getElementById("clanStatisticsSpoiler");
clanStatisticsSpoiler.querySelector("img").style.transform = spoiled ? 'rotate(0deg)' : 'rotate(90deg)';
clanStatisticsSpoiler.title = isEn ? (spoiled ? "Show clan statistics" : "Hide clan statistics") : (spoiled ? "Показать статистику клана" : "Скрыть статистику клана");
document.getElementById("clanStatisticsRow").style.display = spoiled ? "none" : "";
}
function getViewingPlayerLevel() {
if(location.pathname == "/home.php" || location.pathname == "/pl_info.php") {
if(isNewPersonPage) {
const levelInfoCell = Array.from(document.querySelectorAll("div.home_pers_info")).find(x => x.innerHTML.includes(isEn ? "Combat level" : "Боевой уровень"));
return parseInt(levelInfoCell.querySelector("div[id=bartext] > span").innerText);
} else {
const playerLevelExec = new RegExp(`<b>${isEn ? "Combat level" : "Боевой уровень"}: (\\d+?)<\\/b>`).exec(document.documentElement.innerHTML);
if(playerLevelExec) {
return parseInt(playerLevelExec[1]);
}
}
}
return 0;
}
function getPlayerFraction() {
let fractionSourceText;
if(location.pathname == '/home.php') {
const fractionImage = isNewPersonPage ? document.querySelector("div#hwm_no_zoom div.home_main_pers_block center a[href^='castle.php'] img") : document.querySelector("body > center table.wb > tbody > tr:nth-child(2) center a[href^='castle.php'] img");
fractionSourceText = fractionImage?.src;
}
const playerFractionExec = /\/i\/f\/r(\d{1,3})\.png/.exec(fractionSourceText || document.querySelector("body").innerHTML);
if(playerFractionExec) {
return playerFractionExec[1];
}
}
// https://dcdn3.heroeswm.ru/i/april2023/gn_face.png
// https://dcdn.heroeswm.ru/i/april2023/pvp_gerb2.png
// https://dcdn3.heroeswm.ru/i/april2023/gv_face.png
// https://dcdn1.heroeswm.ru/i/april2023/gr_face.png
// https://dcdn1.heroeswm.ru/i/april2023/go_face.png
// https://dcdn.heroeswm.ru/i/kukla_png/premium/1april2023/p8ap.png?v=10.d20e1e
// https://dcdn1.heroeswm.ru/i/kukla_png/premium/1april2023/p1ap.png?v=10.d20e1e
// https://dcdn.heroeswm.ru/i/kukla_png/premium/1april2023/p2ap.png?v=10.d20e1e
// https://dcdn.heroeswm.ru/i/kukla_png/premium/1april2023/p3ap.png?v=10.d20e1e
// https://dcdn3.heroeswm.ru/i/kukla_png/premium/1april2023/p4ap.png?v=10.d20e1e
// https://dcdn3.heroeswm.ru/i/kukla_png/premium/1april2023/p5ap.png?v=10.d20e1e
// https://dcdn2.heroeswm.ru/i/kukla_png/premium/1april2023/p6ap.png?v=10.d20e1e
// https://dcdn2.heroeswm.ru/i/kukla_png/premium/1april2023/p7ap.png?v=10.d20e1e
// https://dcdn.heroeswm.ru/i/kukla_png/premium/1april2023/p9ap.png?v=10.d20e1e
// https://dcdn.heroeswm.ru/i/kukla_png/premium/1april2023/p10ap.png?v=10.d20e1e
// https://dcdn.heroeswm.ru/i/transport/1april/150/2.png
// https://dcdn.heroeswm.ru/i/transport/1april/150/108.png
// https://dcdn.heroeswm.ru/i/transport/1april/150/150.png