hwmSetsMaster

Смена фракций. Наборы оружия, навыков, армий и миниартов мага. Билды

目前为 2025-03-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name hwmSetsMaster
  3. // @author Tamozhnya1
  4. // @namespace Tamozhnya1
  5. // @description Смена фракций. Наборы оружия, навыков, армий и миниартов мага. Билды
  6. // @version 13.6
  7. // @include *heroeswm.ru/*
  8. // @include *lordswm.com/*
  9. // @exclude */rightcol.php*
  10. // @exclude */ch_box.php*
  11. // @exclude */chat*
  12. // @exclude */ticker.html*
  13. // @exclude */frames*
  14. // @exclude */brd.php*
  15. // @require https://update.greasyfork.org/scripts/490927/1360667/Tamozhnya1Lib.js
  16. // @grant GM_getValue
  17. // @grant GM_setValue
  18. // @grant GM_deleteValue
  19. // @grant GM_listValues
  20. // @grant GM.xmlHttpRequest
  21. // @license MIT
  22. // ==/UserScript==
  23.  
  24. if(!PlayerId) {
  25. return;
  26. }
  27. const Strings = {
  28. "ru": {
  29. Army: "Армия",
  30. Save: "Сохранить",
  31. Add: "Добавить",
  32. AddCurrent: "Добавить текущий",
  33. SetName: "Наименование набора",
  34. Delete: "Удалить",
  35. Talents: "Навыки",
  36. Inventory: "Оружие",
  37. RemoveAll: "Снять все",
  38. EnterJpg: "enter0.jpg",
  39. SignInTitle: "Войти",
  40. Castle: "Замок",
  41. Task: "Задание",
  42. Apply: "Применить",
  43. AvailablePoints: "Свободных очков",
  44. AvailableTalentPoints: "Свободных очков от навыка",
  45. IncreaseManyPointsTooltip: "Введите число от ${minValue} до ${maxValue}. Нажмите Tab."
  46. },
  47. "en": {
  48. Army: "Army",
  49. Save: "Save",
  50. Add: "Add",
  51. AddCurrent: "Add current",
  52. SetName: "Set name",
  53. Delete: "Delete",
  54. Talents: "Talents",
  55. Inventory: "Inventory",
  56. RemoveAll: "Un-equip all",
  57. EnterJpg: "enter0_eng.jpg",
  58. SignInTitle: "Sign in",
  59. Castle: "Castle",
  60. Task: "Task",
  61. Apply: "Apply",
  62. AvailablePoints: "Available points",
  63. AvailableTalentPoints: "Available talent points",
  64. IncreaseManyPointsTooltip: "Enter number from ${minValue} to ${maxValue}. Press Tab."
  65. }
  66. };
  67. const LocalizedString = Strings[lang];
  68. let Fraction;
  69. getFraction();
  70. const wizardSkillLevel = getWizardSkillLevel();
  71. const miniArtsPowers = [
  72. [0, 1, 1, 0, 0, 0, 1],
  73. [0, 2, 1, 0, 1, 0, 2],
  74. [0, 3, 2, 0, 1, 0, 3],
  75. [0, 3, 2, 0, 2, 1, 4],
  76. [0, 4, 3, 1, 2, 1, 5],
  77. [0, 4, 4, 1, 3, 1, 6],
  78. [0, 5, 5, 1, 3, 1, 7],
  79. [0, 6, 6, 1, 4, 2, 8],
  80. [0, 6, 6, 2, 4, 2, 9],
  81. [0, 7, 7, 2, 5, 2, 10],
  82. [0, 8, 8, 2, 5, 2, 11],
  83. [0, 9, 9, 2, 6, 2, 12],
  84. [0, 10, 10, 2, 6, 2, 13],
  85. [0, 11, 11, 2, 6, 2, 14],
  86. [0, 12, 12, 2, 6, 2, 15],
  87. ];
  88. const miniArtsPower = miniArtsPowers[wizardSkillLevel];
  89. const creatures = [
  90. {},
  91. { imageSource: "https://dcdn2.heroeswm.ru/i/portraits/mastergremlinanip33.png?v=21" },
  92. { imageSource: "https://dcdn2.heroeswm.ru/i/portraits/obsgargolyanip33.png?v=23" },
  93. { imageSource: "https://dcdn3.heroeswm.ru/i/portraits/steelgolemanip33.png?v=20" },
  94. { imageSource: "https://dcdn.heroeswm.ru/i/portraits/archmageanip33.png?v=18" },
  95. { imageSource: "https://dcdn.heroeswm.ru/i/portraits/djinn_sultananip33.png?v=20" },
  96. { imageSource: "https://dcdn1.heroeswm.ru/i/portraits/rakshasa_rajaanip33.png?v=23" },
  97. { imageSource: "https://dcdn3.heroeswm.ru/i/portraits/titananip33.png?v=21" },
  98. ];
  99. const miniArtsEffects = [
  100. { imageSource: "" },
  101. { imageSource: "//dcdn.heroeswm.ru/i/icons/attr_defense.png?v=1" },
  102. { imageSource: "//dcdn.heroeswm.ru/i/icons/attr_attack.png?v=1" },
  103. { imageSource: "//dcdn.heroeswm.ru/i//icons/attr_speed.png?v=1" },
  104. { imageSource: "//dcdn.heroeswm.ru/i/icons/attr_hit_points.png?v=1" },
  105. { imageSource: "//dcdn.heroeswm.ru/i/icons/attr_morale.png?v=1" },
  106. { imageSource: "//dcdn.heroeswm.ru/i/icons/attr_initiative.png?v=1" },
  107. ];
  108.  
  109. //const homeArtsPanelSelector = doc => isNewPersonPage ? doc.querySelector("div#inv_doll_stats") : getParent(doc.querySelector("div.arts_info.shop_art_info"), "table");
  110. const homeArtsPanelSelector = doc => isNewPersonPage ? doc.querySelector("div#inv_doll_stats") : doc.querySelector("body > center table.wb > tbody > tr:nth-child(3) > td > table:nth-child(2)");
  111. const homeStatsPanelSelector = doc => isNewPersonPage ? doc.getElementById("home_css_stats_wrap_div") : getParent(doc.querySelector("img[src*='attr_attack']"), "table", 2);
  112. const homeArmyPanelSelector = doc => isNewPersonPage ? doc.querySelector("div.home_pers_army") : doc.querySelector("center > div > div.cre_creature72").parentNode;
  113. const playerInfoArtsPanelSelector = doc => getParent(doc.querySelector("div[class^='slot']"), "div");
  114. const playerInfoStatsPanelSelector = doc => getParent(doc.querySelector("img[src*='attr_attack']"), "table");
  115. const playerInfoArmyPanelSelector = doc => doc.querySelector("center > div > div.cre_creature72").parentNode;
  116. const playerInfoPerksPanelSelector = doc => getParent(doc.querySelector("a[href^='showperkinfo.php']"), "table", 2);
  117. const mapHuntButtonsPanelSelector = doc => doc.querySelector("div#neut_right_block div.map_buttons_container");
  118. const mapHuntButtons2PanelSelector = doc => doc.querySelector("div#neut_right_block2 div.map_buttons_container");
  119. const mapMercenaryTaskPanelSelector = doc => getParent(doc.querySelector("div#map_right_block_inside > table.wbwhite.rounded_table.map_table_margin center a[href='mercenary_guild.php']"), "center");
  120. const inventoryStatsPanelSelector = doc => doc.querySelector("div.inventory_stats");
  121. const resourcesPath = `${location.protocol}//${location.host.replace("www", "dcdn")}`;
  122.  
  123. const weaponSetsPreferences = {
  124. menuTitle: LocalizedString.Inventory,
  125. menuImage: `${resourcesPath}/i/combat/btn_inventory.png?v=7`,
  126. setReferencePage: "inventory.php",
  127. sets: new Array(),
  128. menuItems: {},
  129. currentMenuItem: undefined,
  130. initSetsApplyAction: function() {
  131. this.sets.length = 0;
  132. this.sets.push({ number: 0, name: LocalizedString.RemoveAll, method: "GET", url: "/inventory.php?all_off=100" });
  133. const weaponSets = JSON.parse(getPlayerValue("WeaponSets", "{}"));
  134. for(const setNumber in weaponSets) {
  135. this.sets.push({ number: setNumber, name: weaponSets[setNumber], method: "GET", url: `/inventory.php?all_on=${setNumber}` });
  136. }
  137. },
  138. name: "WeaponSet",
  139. getCurrentSetName: function() { return "WeaponSet"; },
  140. onPageLoad: function() {
  141. if(location.pathname == "/inventory.php") {
  142. const undressDiv = document.querySelector("div[id ='undress_all_div']");
  143. addSetChangerListener(undressDiv, this, 0);
  144.  
  145. const setDivs = document.querySelectorAll("div[set_div_id]"); // Если setDivs.length = 0, то ничего не делаем - мы в заявке на бой
  146. if(setDivs.length > 0) {
  147. const weaponSets = {};
  148. for(const setDiv of setDivs) {
  149. const setNumber = setDiv.getAttribute("set_div_id");
  150. if(setDiv.hasAttribute("onclick")) {
  151. weaponSets[setNumber] = setDiv.innerText;
  152. addSetChangerListener(setDiv, this, setNumber);
  153. }
  154. }
  155. setPlayerValue("WeaponSets", JSON.stringify(weaponSets));
  156. }
  157. }
  158. this.findSetChangersAndAddListener();
  159. },
  160. findSetChangersAndAddListener: function() {
  161. let setRefs = document.querySelectorAll("a[href^='inventory.php?all_off=100']");
  162. for(const setRef of setRefs) {
  163. addSetChangerListener(setRef, this, 0);
  164. }
  165. setRefs = document.querySelectorAll("a[href*='inventory.php?all_on=']");
  166. for(const weaponSetReference of setRefs) {
  167. let setNumber = weaponSetReference.getAttribute("href").split("all_on=")[1];
  168. addSetChangerListener(weaponSetReference, this, setNumber);
  169. }
  170. }
  171. };
  172. const skillSetsPreferences = {
  173. menuTitle: LocalizedString.Talents,
  174. menuImage: `${resourcesPath}/i/combat/btn_skills_v3.png?v=7`,
  175. setReferencePage: "skillwheel.php",
  176. sets: new Array(),
  177. menuItems: {},
  178. currentMenuItem: undefined,
  179. onPageLoad: function() {
  180. const setRefs = document.querySelectorAll("a[href^='skillwheel.php?setuserperk']"); // skillwheel.php?setuserperk=1&prace=4&buildid=5 // skillwheel.php?rand=1&setstats=1&param0=20&param1=8&param2=0&param3=2
  181. const pageSets = {};
  182. for(const setRef of setRefs) {
  183. pageSets[setRef.innerHTML] = getUrlParamValue(setRef.href, "buildid");
  184. addSetChangerListener(setRef, this, parseInt(getUrlParamValue(setRef.href, "buildid")));
  185. }
  186. setPlayerFractionValue("SkillSets", JSON.stringify(pageSets));
  187. },
  188. initSetsApplyAction: function () {
  189. const skillSets = JSON.parse(getPlayerFractionValue("SkillSets", "{}"));
  190. this.sets = Object.keys(skillSets).map(x => { return { number: parseInt(skillSets[x]), name: x, method: "GET", url: `/skillwheel.php?setuserperk=1&prace=${Fraction}&buildid=${skillSets[x]}` }; });
  191. },
  192. name: "SkillSet",
  193. getCurrentSetName: function() { return getFractionKey("SkillSet"); },
  194. };
  195. const armySetsPreferences = {
  196. menuTitle: LocalizedString.Army,
  197. menuImage: `${resourcesPath}/i/castle_im/btn_recruit.png`,
  198. setReferencePage: "army.php",
  199. sets: new Array(),
  200. menuItems: {},
  201. currentMenuItem: undefined,
  202. setsTable: null,
  203. name: "ArmySet",
  204. getCurrentSetName: function() { return getFractionKey("ArmySet"); },
  205. initSetsApplyAction: function () {
  206. const armySetsData = JSON.parse(getPlayerFractionValue("ArmySets", "{}"));
  207. const armySets = Object.keys(armySetsData).map(x => { const data = armySetsData[x].split("|"); return { Id: x, Name: data[0], Army: data.slice(1).map(y => Number(y)) } });
  208. this.sets = armySets.map(x => { return { number: parseInt(x.Id), name: x.Name, title: x.Army.join("+"), method: "POST", url: "/army_apply.php", data: "set_id=7&" + x.Army.map((x, i) => `countv${i + 1}=${x}`).join("&") }; });
  209. if(location.pathname == "/army.php") {
  210. this.drawSetsTable(armySets);
  211. }
  212. },
  213. drawSetsTable: function(armySets) {
  214. const isInit = this.setsTable ? false : true;
  215. let container;
  216. if(isInit) {
  217. const hwm_for_zoom = document.getElementById("hwm_for_zoom") || document.getElementById("hwm_no_zoom");
  218. container = document.body;
  219. if(!isMobileInterface) {
  220. container = addElement("center", null, hwm_for_zoom);
  221. }
  222. this.setsTable = addElement("table", { class: "smithTable", style: `${isMobileInterface ? "margin-left: px;" : ""}` }, container);
  223. } else {
  224. this.setsTable.innerHTML = "";
  225. container = this.setsTable.parentNode;
  226. }
  227.  
  228. const cellWidths = [100, 60, 60, 60, 60, 60, 60, 60, 10, 10];
  229. for(const cellWidth of cellWidths) {
  230. this.setsTable.innerHTML += `<col style="width: ${cellWidth}px;" />`;
  231. }
  232. this.drawTableHeader();
  233. for(const armySet of armySets) {
  234. this.drawSetRow(armySet);
  235. }
  236. if(isInit) {
  237. const saveButton = addElement("input", { type: "button", value: LocalizedString.Save, class: "button-62", style: isMobileInterface ? "margin: 0 0 0 50px;" : "" }, container);
  238. saveButton.addEventListener("click", this.saveSets);
  239. const addCurrentButton = addElement("input", { type: "button", value: LocalizedString.AddCurrent, class: "button-62" }, container);
  240. addCurrentButton.addEventListener("click", function() { armySetsPreferences.drawSetRow(); });
  241. }
  242. },
  243. drawTableHeader: function () {
  244. if(!this.setsTable) {
  245. return;
  246. }
  247. const units = win.obj;
  248. //console.log(units)
  249. const tr = addElement("tr", null, this.setsTable);
  250. addElement("th", { innerHTML: LocalizedString.SetName.replace(/ /g, "<br>").replace(/-/g, "-<br>") }, tr);
  251. for(let i = 1; i <= 7; i++) {
  252. addElement("th", { innerHTML: (units[i]?.name || "").replace(/ /g, "<br>").replace(/-/g, "-<br>"), onClick: `ChangeSlider(event, ${i}, 0);` }, tr);
  253. }
  254. addElement("th", { style: "width: 30px;" }, tr);
  255. addElement("th", { style: "width: 30px;" }, tr);
  256. },
  257. drawSetRow: function(armySet) {
  258. const isNew = armySet ? false : true;
  259. const units = win.obj;
  260. //console.log(`armySet: ${armySet}, units: ${units}`);
  261. //console.log(units.slice(1));
  262. armySet = armySet || { Id: (new Date()).getTime(), Name: "", Army: [units[1], units[2], units[3], units[4], units[5], units[6], units[7]].map(x => x?.nownumberd || 0) };
  263. if(!this.setsTable) {
  264. return;
  265. }
  266. const tr = addElement("tr", { setId: armySet.Id }, this.setsTable);
  267. let td = addElement("td", {}, tr);
  268. let input = addElement("input", { value: armySet.Name, onfocus: `this.select();`, style: "width: 100%;" }, td);
  269. for(let i = 0; i < armySet.Army.length; i++) {
  270. td = addElement("td", {}, tr);
  271. input = addElement("input", { value: armySet.Army[i], onfocus: `ChangeSlider(event, ${i + 1}, 0); this.select();`, type: "number", style: "min-width: 47px; width: 100%; text-align: right;" }, td);
  272. }
  273. td = addElement("td", {}, tr);
  274. let delButton = addElement("input", { type: "button", value: "x", class: "button-62", style: "width: 100%;", title: LocalizedString.Delete }, td);
  275. delButton.addEventListener("click", this.deleteSet);
  276.  
  277. td = addElement("td", {}, tr);
  278. if(!isNew) {
  279. let applyButton = addElement("input", { type : "button", value : "v", class: "button-62", style: "width: 100%;", title : LocalizedString.Apply }, td);
  280. applyButton.addEventListener("click", function() {
  281. armySetsPreferences.saveSets();
  282. const data = armySet.Army.reduce((x, y, i) => `${x}&countv${i + 1}=${y}`, "set_id=7");
  283. //console.log(data);
  284. applySet(null, armySetsPreferences, {
  285. number: parseInt(armySet.Id),
  286. name: armySet.Name,
  287. title: armySet.Army.join("+"),
  288. method: "POST", url: "/army_apply.php",
  289. data: data
  290. });
  291. });
  292. }
  293. },
  294. saveSets: function () {
  295. const rows = Array.from(armySetsPreferences.setsTable.rows).slice(1);
  296. const armySetsData = rows.reduce((t, x) => ({ ...t, [x.getAttribute("setId")]: Array.from(x.cells).slice(0, 8).map(y => y.firstChild.value).join("|") }), {});
  297. setPlayerFractionValue("ArmySets", JSON.stringify(armySetsData));
  298. },
  299. deleteSet: function () {
  300. const table = this.parentNode.parentNode.parentNode;
  301. const row = this.parentNode.parentNode;
  302. table.removeChild(row);
  303. },
  304. };
  305. const fractionsPreferences = {
  306. menuTitle: LocalizedString.Castle,
  307. menuImage: `${resourcesPath}/i/castle_im/btn_fraction.png`,
  308. setReferencePage: "castle.php",
  309. sets: [],
  310. menuItems: {},
  311. currentMenuItem: undefined,
  312. initSetsApplyAction: function() {
  313. const fractions = JSON.parse(getPlayerValue("Fractions", "{}"));
  314. this.sets = Object.keys(fractions).map(x => { return { number: fractions[x], name: x, method: "GET", url: `/castle.php?change_clr_to=${fractions[x]}&sign=${getPlayerValue("Sign")}` }; });
  315. },
  316. name: "Fraction",
  317. getCurrentSetName: function() { return "Fraction"; },
  318. onPageLoad: async function () {
  319. await this.initCastlesList();
  320. this.findSetChangersAndAddListener();
  321. },
  322. initCastlesList: async function () {
  323. if(location.pathname == '/castle.php' || !getPlayerValue("Fractions")) {
  324. const doc = location.pathname == '/castle.php' ? document : await getRequest("/castle.php");
  325. const fractions = Array.from(doc.querySelectorAll("div.castle_faction_div_inside")).reduce((t, x) => ({...t, [x.getAttribute("hint")]: getUrlParamValue(x.firstChild.href, "show_castle_f") }), {});
  326. setPlayerValue("Fractions", JSON.stringify(fractions));
  327. const changeCastleRef = doc.querySelector("div.castle_yes_no_dialog a[href*='castle.php?change_clr_to']");
  328. if(changeCastleRef) {
  329. setPlayerValue("Sign", getUrlParamValue(changeCastleRef.href, "sign"));
  330. }
  331. }
  332. },
  333. findSetChangersAndAddListener: function() {
  334. const setRefs = document.querySelectorAll("a[href*='castle.php?change_clr_to']");
  335. for(const setRef of setRefs) {
  336. addSetChangerListener(setRef, this, getUrlParamValue(setRef.href, "change_clr_to"));
  337. }
  338. },
  339. setChanged: function(newSetNumber) {
  340. Fraction = newSetNumber;
  341. setPlayerValue("Fraction", Fraction);
  342. createMenu();
  343. }
  344. };
  345. const miniartsPreferences = {
  346. menuTitle: isEn ? "Mini arts" : "Миниарты",
  347. menuImage: `${resourcesPath}/i/castle_im/btn_miniart.png`,
  348. setReferencePage: "magearts.php",
  349. sets: [],
  350. menuItems: {},
  351. currentMenuItem: undefined,
  352. initSetsApplyAction: function() {
  353. const setNumbers = GM_listValues().filter(x => x.startsWith("MiniartsSet_") && x.endsWith(PlayerId)).map(x => x.replace("MiniartsSet_", "").replace(new RegExp(`${PlayerId}$`), ""));
  354. this.sets = setNumbers.map(x => ({ number: x, name: getPlayerValue(`MiniartsSet_${x}`), items: JSON.parse(getPlayerValue(`MiniartsSetItems_${x}`, "[]")) }));
  355. if(location.pathname == "/magearts.php") {
  356. this.miniartSetsTableDatabind();
  357. }
  358. },
  359. name: "MiniartsSet",
  360. getCurrentSetName: function() { return "MiniartsSet"; },
  361. miniartSetsTableDatabind: async function() {
  362. if(location.pathname != "/magearts.php") {
  363. return;
  364. }
  365. const buildTable = Array.from(document.querySelectorAll('b')).find(x => x.innerHTML == (isEn ? "Assemble a new mini-artifact." : 'Собрать новый мини-артефакт.'))?.closest("table");
  366. if(!buildTable) {
  367. return;
  368. }
  369. let miniartSetsTable = document.getElementById("miniartSetsTable");
  370. if(!miniartSetsTable) {
  371. miniartSetsTable = addElement('table', { id: "miniartSetsTable", class: `miniartSetsTable ${buildTable.className}` });
  372. buildTable.insertAdjacentElement("afterend", miniartSetsTable);
  373. miniartSetsTable.width = buildTable.width;
  374. miniartSetsTable.align = buildTable.align;
  375. } else {
  376. miniartSetsTable.innerHTML = "";
  377. }
  378. const currentSet = await this.getCurrentMiniartsSet();
  379. const currentSetNumber = currentSet.reduce((t, x) => t + `${x.unitNumber}${x.code}`, "");
  380. const setCodes = GM_listValues().filter(x => x.startsWith("MiniartsSet_") && x.endsWith(PlayerId)).map(x => x.replace("MiniartsSet_", "").replace(new RegExp(`${PlayerId}$`), ""));
  381. console.log(setCodes);
  382. const sets = setCodes.map(x => ({ number: x, name: getPlayerValue(`MiniartsSet_${x}`), items: JSON.parse(getPlayerValue(`MiniartsSetItems_${x}`, "[]")) }));
  383. if(setCodes.find(x => x == currentSetNumber)) {
  384. setPlayerValue(this.getCurrentSetName(), currentSetNumber);
  385. } else {
  386. sets.push({ number: currentSetNumber, name: "", items: currentSet, isNew: true });
  387. }
  388. console.log(sets);
  389. const restriction = Array.from(document.getElementsByTagName('font')).find(x => x.innerHTML == "Вы находитесь в заявке на бой. Ваши действия ограничены!") != null;
  390. for(const set of sets) {
  391. const setViewHtml = this.getSetViewHtml(set);
  392. const row = addElement('tr', { setCode: set.number, class: set.number == currentSetNumber ? "wblight" : "wbwhite" }, miniartSetsTable);
  393. row.innerHTML = `<td>${setViewHtml}</td>
  394. <td style="border: 1px solid #5D413A; text-align: center;">
  395. <input type="text" name="setNameInput" value="${set.name}" placeholder="${isEn ? "Enter set name" : "Введите название набора"}" style="width: 173px;" />
  396. </td>
  397. <td style="border: 1px solid #5D413A; text-align: center;">
  398. <input type="button" value="${isEn ? "Save" : "Сохранить"}" name="saveButton" class="button-62" />
  399. </td>
  400. <td style="border: 1px solid #5D413A; text-align: center;">
  401. <input type="button" value="${isEn ? "Delete" : "Удалить"}" name="deleteButton" class="button-62" ${set.isNew ? "disabled" : ""} />
  402. </td>`;
  403. row.querySelector(`input[name=setNameInput]`).addEventListener("change", function(e) { set.name = e.target.value; });
  404. row.querySelector(`input[name=saveButton]`).addEventListener("click", function(e) { miniartsPreferences.saveMiniartsSet(e, set); });
  405. row.querySelector(`input[name=deleteButton]`).addEventListener("click", function() { miniartsPreferences.deleteMiniartsSet(set.number); });
  406. }
  407. Array.from(document.querySelectorAll("a[href*='magearts.php?sale=1&id=']")).forEach(x => {
  408. const artId = getUrlParamValue(x.href, "id");// console.log(`artId: ${artId}`);
  409. const usedSets = sets.filter(x => x.items.map(y => y.id).includes(artId));
  410. const isUsed = usedSets.length > 0;
  411. x.style.color = isUsed ? '#882C08' : '#598808';
  412. x.title = isUsed ? 'Входит в сеты: ' + usedSets.map(y => y.name).join(", ") : "";
  413.  
  414. x.parentNode.parentNode.addEventListener("mouseover", function() {
  415. Array.from(document.querySelectorAll(`div[artId='${artId}']`)).forEach(x => { x.style.background = "#FFDDDD"; });
  416. });
  417. x.parentNode.parentNode.addEventListener("mouseout", function() {
  418. Array.from(document.querySelectorAll(`div[artId='${artId}']`)).forEach(x => { x.style.background = "linear-gradient(to top, #fff1eb 0%, #ace0f9 100%)"; });
  419. });
  420. });
  421. },
  422. getSetViewHtml: function(set) {
  423. const destructableArts = Array.from(document.querySelectorAll("a[href^='/magearts.php?sale=1&id=']")).map(x => getUrlParamValue(x.href, "id"));// console.log(destructableArts);
  424. let viewHtml = "";
  425. for(let j = 1; j <= 7; j++) {
  426. const item = set.items.find(x => x.unitNumber == j);
  427. const creature = creatures[j];
  428. let miniArtsHtml = "";
  429. let background = "linear-gradient(to top, #fff1eb 0%, #ace0f9 100%)";
  430. if(item) {
  431. for(let effectIndex = 0; effectIndex < item.code.length; effectIndex++) {
  432. const effectNumber = parseInt(item.code.substr(effectIndex, 1));
  433. const miniArtsEffect = miniArtsEffects[effectNumber];
  434. miniArtsHtml += `<div style="position: relative; display: inline-block; background-color: lightgreen; width: 20px; height: 20px;">
  435. <img src="${miniArtsEffect.imageSource}" style="width: 20px; height: 20px;" />
  436. <div style="position: absolute; right: 0px; bottom: 0px; font-weight: bold; color: #f5c140; text-align: right; text-shadow: 0 0 3px #000,0 0 3px #000,0 0 3px #000,0 0 3px #000;">${miniArtsPower[effectNumber]}</div>
  437. </div>`;
  438. }
  439. if(!destructableArts.includes(item.id) && location.pathname == "/magearts.php") {
  440. background = '#FF9999';
  441. }
  442. }
  443. viewHtml += `<div ${item ? "artId=" + item.id : ""} class="hwm_creature_slider_army1" style="width: 60px; height: 60px; background: ${background};">
  444. <div class="hwm_recruit_mon_parent1">
  445. <img src="${creature.imageSource}" style="position: relative; top: 0; left: 0; image-rendering: -webkit-optimize-contrast; width: 60px; height: auto;">
  446. </div>
  447. <div style="position: absolute; right: 0px; bottom: 0px;">${miniArtsHtml}</div>
  448. </div>`;
  449. }
  450. return viewHtml;
  451. },
  452. saveMiniartsSet: function(e, set) {
  453. if(!set.name) {
  454. e.target.closest("tr").querySelector("input[name=setNameInput]").focus();
  455. return;
  456. }
  457. setPlayerValue(`MiniartsSetItems_${set.number}`, JSON.stringify(set.items));
  458. setPlayerValue(`MiniartsSet_${set.number}`, set.name);
  459. this.miniartSetsTableDatabind();
  460. },
  461. deleteMiniartsSet: function(number) {
  462. deletePlayerValue(`MiniartsSet_${number}`);
  463. deletePlayerValue(`MiniartsSetItems_${number}`);
  464. this.miniartSetsTableDatabind();
  465. },
  466. getCurrentMiniartsSet: async function() {
  467. const unitNames = isEn ? { "Gremlins": 1, "Gremlin engineers": 1, "Gargoyles": 2, "Enchanted gargoyles": 2, "Golems": 3, "Modern golems": 3, "Magi": 4, "Lorekeepers": 4, "Genies": 5, "Senior genies": 5, "Sphynx guardians": 6, "Sphynx warriors": 6, "Giants": 7, "Titans": 7 }
  468. : { "Гремлины": 1, "Старшие гремлины": 1, "Каменные горгульи": 2, "Обсидиановые горгульи": 2, "Железные големы": 3, "Стальные големы": 3, "Маги": 4, "Архимаги": 4, "Джинны": 5, "Джинны-султаны": 5, "Принцессы ракшас": 6, "Раджи ракшас": 6, "Колоссы": 7, "Титаны": 7 };
  469. const artProperties = isEn ? { "Defense": 1, "Attack": 2, "Speed": 3, "Health": 4, "Morale": 5, "Initiative": 6 } : { "Защита": 1, "Нападение": 2, "Скорость": 3, "Здоровье": 4, "Боевой дух": 5, "Инициатива": 6 };
  470. const doc = location.pathname == "/magearts.php" ? document : await getRequest("/magearts.php");
  471. const dressedArts = Array.from(doc.querySelectorAll("input[type=submit][value=' Ok ']")).map(x => {
  472. const row = x.closest("tr");
  473. const maid = row.querySelector("input[type=hidden][name=maid]").value;
  474. const unitName = row.cells[1].querySelector("b")?.innerText.trimEnd() || ""; // console.log(unitName)
  475. const code = Array.from(row.cells[0].querySelectorAll("img")).map(y => artProperties[y.title]).join("");
  476. return { id: maid, unitNumber: unitNames[unitName], code: code };
  477. }).filter(x => x.unitNumber);
  478. //console.log(dressedArts);
  479. return dressedArts;
  480. },
  481. applySet: async function(set) {
  482. console.log(set);
  483. const currentSet = await this.getCurrentMiniartsSet();
  484. for(let i = 1; i <= 7; i++) {
  485. const currentUnitArt = currentSet.find(x => x.unitNumber == i);
  486. const newUnitArt = set.items.find(x => x.unitNumber == i);
  487. if(newUnitArt) {
  488. if(!currentUnitArt || currentUnitArt.id != newUnitArt.id) {
  489. //console.log(`data: dress=1&maid=${newUnitArt.id}&who=${newUnitArt.unitNumber}`);
  490. await postRequest("/magearts.php", `dress=1&maid=${newUnitArt.id}&who=${newUnitArt.unitNumber}`);
  491. }
  492. } else {
  493. if(currentUnitArt && !set.items.find(x => x.id == currentUnitArt.id)) {
  494. //console.log(`data: dress=1&maid=${currentUnitArt.id}&who=0`);
  495. await postRequest("/magearts.php", `dress=1&maid=${currentUnitArt.id}&who=0`);
  496. }
  497. }
  498. }
  499. },
  500. menuEnabledCondition: function() { return Fraction == "3"; },
  501. getTooltip: function(set) { return this.getSetViewHtml(set); }
  502. };
  503.  
  504. const preferences = [miniartsPreferences, weaponSetsPreferences, skillSetsPreferences, armySetsPreferences, fractionsPreferences];
  505. const visibleMunuItems = [[], [], [], []];
  506. const dropdownPositions = { bottom: 1, right: 2 };
  507. const dropdownActivateMethods = { hover: 1, click: 2 };
  508.  
  509. main();
  510. function main() {
  511. if(isHeartOnPage && Fraction) {
  512. addStyle(`
  513. .button-62 {
  514. background: linear-gradient(to bottom right, #E47B8E, #FF9A5A);
  515. border: 0;
  516. border-radius: 5px;
  517. color: #FFFFFF;
  518. cursor: pointer;
  519. display: inline-block;
  520. font-family: -apple-system,system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  521. font-size: 16px;
  522. font-weight: 500;
  523. outline: transparent;
  524. padding: 0 5px;
  525. text-align: center;
  526. text-decoration: none;
  527. transition: box-shadow .2s ease-in-out;
  528. user-select: none;
  529. -webkit-user-select: none;
  530. touch-action: manipulation;
  531. white-space: nowrap;
  532. }
  533.  
  534. .button-62:not([disabled]):focus {
  535. box-shadow: 0 0 .25rem rgba(0, 0, 0, 0.5), -.125rem -.125rem 1rem rgba(239, 71, 101, 0.5), .125rem .125rem 1rem rgba(255, 154, 90, 0.5);
  536. }
  537.  
  538. .button-62:not([disabled]):hover {
  539. box-shadow: 0 0 .25rem rgba(0, 0, 0, 0.5), -.125rem -.125rem 1rem rgba(239, 71, 101, 0.5), .125rem .125rem 1rem rgba(255, 154, 90, 0.5);
  540. }
  541. .button-62:disabled,button[disabled] {
  542. background: linear-gradient(177.9deg, rgb(58, 62, 88) 3.6%, rgb(119, 127, 148) 105.8%);
  543. }
  544. table.smithTable {
  545. width: 100%;
  546. background: BurlyWood;
  547. border: 5px solid BurlyWood;
  548. border-radius: 5px;
  549. margin-top: 1px;
  550. }
  551. table.smithTable th {
  552. border: 1px none #f5c137;
  553. overflow: hidden;
  554. text-align: center;
  555. font-size: 11px;
  556. }
  557. table.smithTable td {
  558. border: 1px none #f5c137;
  559. overflow: hidden;
  560. text-align: center;
  561. }
  562. table.smithTable tr:nth-child(odd) {
  563. background: Wheat;
  564. }
  565. table.smithTable tr:nth-child(even) {
  566. background: white;
  567. }
  568. .waiting {
  569. cursor: wait;
  570. }
  571. .not-allowed {
  572. cursor: not-allowed;
  573. }
  574. .hwm_creature_slider_army1 {
  575. font-size: 100%;
  576. width: 60px;
  577. max-width: 60px;
  578. position: relative;
  579. display: table-cell;
  580. letter-spacing: normal;
  581. -moz-user-select: none;
  582. -webkit-user-select: none;
  583. -ms-user-select: none;
  584. }
  585. .hwm_recruit_mon_parent1 {
  586. padding-top: 5%;
  587. position: relative;
  588. top: 0;
  589. left: 0;
  590. }
  591. .hwm_recruit_mon_parent1 img {
  592. position: absolute;
  593. top: 0;
  594. left: 0;
  595. width: 100%;
  596. height: 100%;
  597. }
  598. table.miniartSetsTable {
  599.  
  600. }
  601. table.miniartSetsTable td {
  602. border: '1px solid #5D413A',
  603. text-align: "center"
  604. }
  605. .miniartsMenuContainer {
  606. position: absolute;
  607. z-index: 150;
  608. text-align: left;
  609. background: #6b6b69;
  610. color: #f5c137;
  611. border: 1px solid #f5c137;
  612. padding: 2px 5px;
  613. font-weight: bold;
  614. }
  615. .miniartsMenuContainer div {
  616. cursor: pointer;
  617. }
  618. `);
  619. //updateOldData();
  620. for(const preference of preferences) {
  621. if(preference.onPageLoad) {
  622. preference.onPageLoad();
  623. }
  624. }
  625. createMenu();
  626. window.addEventListener("resize", function() { createMenu(true); });
  627. if(isMobileDevice) {
  628. window.addEventListener("click", function(e) {
  629. const closestDropdown = e.target.closest("div[name$=Dropdown]");
  630. if(!closestDropdown) {
  631. [...document.querySelectorAll("div[name$=Dropdown]")].forEach(x => x.style.display = "none");
  632. }
  633. const closestExpandable = e.target.closest("div[id$=_expandable]");
  634. if(!closestExpandable) {
  635. [...document.querySelectorAll("div[id$=_expandable]")].forEach(x => x.style.display = "none");
  636. }
  637. const closestBreadcrumb = e.target.closest("div#breadcrumbs");
  638. if(!closestBreadcrumb) {
  639. [...document.querySelectorAll("div#breadcrumbs li.subnav ul")].forEach(y => y.style.display = "none");
  640. }
  641. });
  642. [...document.querySelectorAll("div#breadcrumbs")].forEach(x => x.addEventListener("touchstart", function(e) {
  643. [...document.querySelectorAll("div#breadcrumbs li.subnav ul")].forEach(y => y.style.display = "none");
  644. this.querySelector("li.subnav ul").style.display = "block";
  645. }));
  646. }
  647. const menuPanel = document.querySelector("div#hwm_header") || document.querySelector("#main_top_table") || document.querySelector("body > table");
  648. const homeRef = menuPanel.querySelector("a[href='home.php']");
  649. const menuAnchor = isNewInterface ? homeRef.parentNode : getParent(homeRef, "table", 3);
  650. const menuContainer = menuAnchor.parentNode;
  651. observe(menuContainer, function() { createMenu(true); });
  652. drowSkillChangers();
  653. if(location.pathname == '/home.php' && isNewPersonPage) {
  654. observe(document.querySelector("div#home_css_stats_wrap_div"), drowSkillChangers);
  655. }
  656. }
  657. }
  658. function updateOldData() {
  659. const fractionNumbers = [1, 101, 2, 102, 3, 103, 4, 104, 5, 105, 205, 6, 106, 7, 107, 8, 108, 9, 10];
  660. for(const fractionNumber of fractionNumbers) {
  661. //deletePlayerFractionValue("SkillSets", fractionNumber);
  662. const skillSetsOld = getValue(`SkillSets${PlayerId}Fraction${fractionNumber}`);
  663. const skillSets = getPlayerFractionValue("SkillSets", undefined, fractionNumber);
  664. // if(fractionNumber == 8) {
  665. // console.log(`fractionNumber: ${fractionNumber}, skillSetsOld: ${skillSetsOld}, skillSets: ${skillSets}, CurrentSkillSet: ${getValue(`CurrentSkillSet${PlayerId}Fraction${fractionNumber}`)}`);
  666. // }
  667. if(skillSetsOld && !skillSets) {
  668. setPlayerFractionValue("SkillSets", skillSetsOld, fractionNumber);
  669. setPlayerFractionValue("SkillSet", getValue(`CurrentSkillSet${PlayerId}Fraction${fractionNumber}`), fractionNumber);
  670. }
  671. //deletePlayerFractionValue("ArmySets", fractionNumber);
  672. const armySetsOld = getValue(`ArmySets${PlayerId}Fraction${fractionNumber}`);
  673. const armySets = getPlayerFractionValue("ArmySets", undefined, fractionNumber);
  674. // if(fractionNumber == 8) {
  675. // console.log(`fractionNumber: ${fractionNumber}, armySetsOld: ${armySetsOld}, armySets: ${armySets}, ArmySet: ${getValue(`ArmySet${PlayerId}Fraction${fractionNumber}`)}`);
  676. // }
  677. if(armySetsOld && !armySets) {
  678. setPlayerFractionValue("ArmySets", armySetsOld, fractionNumber);
  679. setPlayerFractionValue("ArmySet", getValue(`ArmySet${PlayerId}Fraction${fractionNumber}`), fractionNumber);
  680. }
  681. // if(fractionNumber == 8) {
  682. // console.log(`ArmySet: ${getPlayerFractionValue("ArmySet", undefined, fractionNumber)}`);
  683. // }
  684. }
  685. }
  686. function getTextWidth(text, font) {
  687. const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas")); // re-use canvas object for better performance
  688. const context = canvas.getContext("2d");
  689. context.font = font;
  690. const metrics = context.measureText(text);
  691. return metrics.width;
  692. }
  693. function getCssStyle(element, prop) { return window.getComputedStyle(element, null).getPropertyValue(prop); }
  694. function getCanvasFont(el = document.body) {
  695. const fontWeight = getCssStyle(el, 'font-weight') || 'normal';
  696. const fontSize = getCssStyle(el, 'font-size') || '16px';
  697. const fontFamily = getCssStyle(el, 'font-family') || 'Times New Roman';
  698. return `${fontWeight} ${fontSize} ${fontFamily}`;
  699. }
  700. function createMenu(isResize) {
  701. const menuPanel = document.querySelector("div#hwm_header") || document.querySelector("#main_top_table") || document.querySelector("body > table");
  702. if(!menuPanel) {
  703. armySetsPreferences.initSetsApplyAction();
  704. return;
  705. }
  706. const setsMenuPosition = getPlayerBool("ShowMenyAtRight") ? "right" : "left"; //const setsMenuPosition = isNewInterface && isMobileDevice || getPlayerBool("ShowMenyAtRight") ? "right" : "left";
  707. const homeRef = menuPanel.querySelector("a[href='home.php']");
  708. const framesRef = menuPanel.querySelector("a[href='frames.php']");
  709. const menuAnchor = setsMenuPosition == "left" ? (isNewInterface ? homeRef.parentNode : getParent(homeRef, "table", 3)) : (isNewInterface ? framesRef.parentNode : getParent(framesRef, "table", 3));
  710. if(!menuAnchor) {
  711. return;
  712. }
  713. const menuContainer = menuAnchor.parentNode;
  714. const menuAnchorStyle = window.getComputedStyle(menuAnchor);
  715. const menuContainerStyle = window.getComputedStyle(menuContainer);// console.log(menuContainerStyle);
  716. if(!isNewInterface) {
  717. menuContainer.style.position = "relative";
  718. }
  719. const marginTop = menuAnchorStyle.marginTop;
  720. const superSets = JSON.parse(getPlayerValue("SuperSets", "[]"));
  721. const menuContainerRect = menuContainer.getBoundingClientRect();
  722. const anchorRect = menuAnchor.getBoundingClientRect();
  723. //console.log(anchorRect);
  724. const borderWidth = isNewInterface ? 1 : 2;
  725. const menuItemHeight = anchorRect.height - borderWidth * 2;
  726. const menuItemLineHeight = menuItemHeight - borderWidth;
  727. const foreColor = "#f5c137";
  728. const backgroundColor = isNewInterface ? "linear-gradient(to top, #09203f 0%, #537895 100%)" : (document.querySelector("img[src*='i/top_ny']") ? "#003399" : "#6b6b69");
  729. const zIndex = location.pathname == "/photo_pl_photos.php" ? "0" : "100";
  730. let currentMenuItemLeft = (setsMenuPosition == "left" ? anchorRect.left : anchorRect.right) - menuContainerRect.left;
  731. if(!isNewInterface) {
  732. currentMenuItemLeft -= setsMenuPosition == "left" ? 4 : -4; //setsMenuPosition == "left" ? (anchorRect.left - borderWidth - 1) : (anchorRect.right + borderWidth + 1);
  733. }
  734. let previousWidth = 0;
  735. for(let i = preferences.length - 1; i >= 0; i--) {
  736. const currentPreferences = preferences[i];
  737. if(currentPreferences.menuEnabledCondition && !currentPreferences.menuEnabledCondition()) {
  738. continue;
  739. }
  740. const mainMenuItemId = `SetsMenuItem${i}`;
  741. let mainMenuItem = document.getElementById(mainMenuItemId);
  742. let selectedValueHidden = document.getElementById(`hwmSetsMaster${currentPreferences.name}SelectedValue`);
  743. if(!mainMenuItem) {
  744. const menuHeaderStyle = `top: 0px; margin-top: ${marginTop}; min-width: ${menuItemHeight}px; font-size: 9pt; position: absolute; border-radius: 5px; background: ${backgroundColor}; color: ${foreColor}; border: ${borderWidth}px solid ${foreColor}; padding: 0 3px 0 3px; font-weight: bold; text-align: center; z-index: ${zIndex};`;
  745. //console.log(menuHeaderStyle);
  746. mainMenuItem = addElement("div", { id: mainMenuItemId, name: "setsMenuItem", style: menuHeaderStyle }, menuContainer);
  747. // console.log(mainMenuItem.name)
  748. // console.log(mainMenuItem.getAttribute("name"))
  749. let itemContent = currentPreferences.menuTitle;
  750. let itemTitle = "";
  751. if(currentPreferences.menuImage && isNewInterface) {
  752. itemTitle = currentPreferences.menuTitle;
  753. itemContent = `<img src="${currentPreferences.menuImage}" alt="${itemTitle}" style="height: 90%; margin-top: 2px; border-radius: 50%;">`;
  754. }
  755. const itemChild = addElement("a", { innerHTML: itemContent, href: currentPreferences.setReferencePage, style: `color: ${foreColor}; text-decoration: none; vertical-align: middle;` }, mainMenuItem);
  756. if(itemTitle != "") {
  757. itemChild.title = itemTitle;
  758. }
  759. selectedValueHidden = addElement("div", { id: `hwmSetsMaster${currentPreferences.name}SelectedValue`, hidden: "hidden" }, mainMenuItem);
  760. if(currentPreferences == fractionsPreferences) {
  761. createBuildButton();
  762. }
  763. visibleMunuItems[i] = [mainMenuItem];
  764. }
  765. mainMenuItem.style.height = `${menuItemHeight}px`;
  766. mainMenuItem.style.lineHeight = `${menuItemLineHeight}px`;
  767. const mainMenuItemRect = mainMenuItem.getBoundingClientRect();
  768. currentMenuItemLeft = currentMenuItemLeft + (setsMenuPosition == "left" ? (- mainMenuItemRect.width) : previousWidth);
  769. previousWidth = mainMenuItemRect.width;
  770. mainMenuItem.style.left = `${currentMenuItemLeft}px`;
  771.  
  772. const menuContent = getOrCreateAndResizeDropdown(i, mainMenuItem, ` z-index: ${zIndex}; list-style-position: inside; color: ${foreColor}; padding: 2px 3px 2px 3px; white-space: nowrap; background: ${backgroundColor}; line-height: normal;`);
  773. if(isResize) {
  774. continue;
  775. }
  776. currentPreferences.initSetsApplyAction();
  777. menuContent.style.display = "block"; // Перед заполнением покажем див для правильного определения его размеров (нужно, если он установлен в none)
  778. menuContent.innerHTML = '';
  779. const currentSetNumber = getPlayerValue(currentPreferences.getCurrentSetName(), -1);
  780. let maxClientWidth = menuContent.clientWidth;
  781. if(currentPreferences.getTooltip) {
  782. addElement("div", { id: `${mainMenuItem.id}TooltipContainer`, style: `position: absolute; top: 0px; left: 0px; display: none;`}, menuContent);
  783. }
  784. for(const currentSet of currentPreferences.sets) {
  785. const dropDownMenuItem = addElement("li", { type: "disc", style: "text-align: left;" }, menuContent);
  786. const currentSetReference = addElement("b", { id: `${currentPreferences.getCurrentSetName()}SetReference${currentSet.number}`, name: `${currentPreferences.getCurrentSetName()}SetReference`, innerHTML: currentSet.name, title: currentSet.title || "", style: `color: ${foreColor}; cursor: pointer; position: relative;` }, dropDownMenuItem);
  787. currentSetReference.addEventListener("click", function() { applySet(currentSetReference, currentPreferences, currentSet); });
  788. if(currentPreferences.getTooltip) {
  789. const tooltip = currentPreferences.getTooltip(currentSet);
  790. currentSetReference.addEventListener("mouseenter", function(e) {
  791. const tooltipContainer = document.getElementById(`${mainMenuItem.id}TooltipContainer`);
  792. tooltipContainer.innerHTML = tooltip;
  793. tooltipContainer.style.display = "";
  794. const anchorRect = tooltipContainer.parentNode.getBoundingClientRect();
  795. tooltipContainer.style.left = `${anchorRect.width}px`;
  796. tooltipContainer.style.top = `0px`;
  797. });
  798. currentSetReference.addEventListener("mouseleave", function() { document.getElementById(`${mainMenuItem.id}TooltipContainer`).style.display = "none"; });
  799. }
  800. if(currentSet.number == currentSetNumber) {
  801. selectedValueHidden.innerHTML = currentSet.number;
  802. markCurrent(currentSetReference, currentPreferences, currentSet.number);
  803. }
  804. currentPreferences.menuItems[currentSet.number] = currentSetReference;
  805.  
  806. if(currentPreferences == fractionsPreferences) {
  807. const fractionBuilds = superSets.filter(x => x.Fraction == currentSet.number);
  808. if(fractionBuilds.length > 0) {
  809. let dropdownActivateMethod = dropdownActivateMethods.hover;
  810. let buildsActivator = currentSetReference;
  811. if(isMobileDevice) {
  812. dropdownActivateMethod = dropdownActivateMethods.click;
  813. buildsActivator = addElement("div", { id: `${currentSetReference.id}BuildsActivator`, name: `${currentSetReference.getAttribute("name")}BuildsActivator`, style: "display: inline-block; cursor: pointer; position: relative;", innerHTML: `<img src="https://dcdn.heroeswm.ru/i/inv_im/btn_expand.svg" style="vertical-align: middle;">` }, currentSetReference);
  814. }
  815. const superDropdown = getOrCreateAndResizeDropdown(i, buildsActivator, ` z-index: ${Number(zIndex) + 1}; list-style-position: inside; color: ${foreColor}; padding: 2px 3px 2px 3px; white-space: nowrap; background: ${backgroundColor};`, dropdownPositions.right, dropdownActivateMethod);
  816. for(const build of fractionBuilds) {
  817. const superDropdownMenuItem = addElement("li", { type: "disc", style: "text-align: left;" }, superDropdown);
  818. const html = `${build.Name} <span id="deleteBuild${build.Id}Button" title='${isEn ? "Delete" : "Удалить"}' style="cursor: pointer; display: inline; color: yellow;">&times;</span>`
  819. const superDropdownText = addElement("b", { name: "superDropdownText", innerHTML: html, style: `color: ${foreColor}; cursor: pointer;` }, superDropdownMenuItem);
  820. document.getElementById(`deleteBuild${build.Id}Button`).addEventListener("click", function(e) { e.stopPropagation(); deleteBuild(build.Id, superDropdownMenuItem); });
  821. superDropdownMenuItem.addEventListener("click", function(e) { e.stopPropagation(); applyBuild(superDropdownText, build); });
  822. if(build.Id == getPlayerValue("SuperSet")) {
  823. superDropdownText.style.color = '#0f0';
  824. }
  825. }
  826. superDropdown.style.display = "none";
  827. }
  828. }
  829. let currentWidth = getTextWidth(currentSet.name, getCanvasFont(currentSetReference));
  830. if(maxClientWidth < currentWidth) {
  831. maxClientWidth = currentWidth;
  832. }
  833. }
  834. if(currentPreferences == fractionsPreferences) {
  835. const showAddSuperSetButtonLable = addElement("label", { for: "showAddSuperSetButtonCheckbox", innerText: (isEn ? 'Show button "Create super set"' : 'Показать кнопку "Создать билд"') + "\t" }, menuContent);
  836. const showAddSuperSetButtonCheckbox = addElement("input", { id: "showAddSuperSetButtonCheckbox", type: "checkbox" }, menuContent);
  837. showAddSuperSetButtonCheckbox.checked = getPlayerBool("ShowAddSuperSetButton", true);
  838. showAddSuperSetButtonCheckbox.addEventListener("change", function() { setPlayerValue("ShowAddSuperSetButton", this.checked); }, false);
  839. addElement("br", {}, menuContent);
  840.  
  841. const showMenyAtRightLable = addElement("label", { for: "showMenyAtRightCheckbox", innerText: (isEn ? 'Show menu at right' : 'Показать меню справа') + "\t" }, menuContent);
  842. const showMenyAtRightCheckbox = addElement("input", { id: "showMenyAtRightCheckbox", type: "checkbox" }, menuContent);
  843. showMenyAtRightCheckbox.checked = getPlayerBool("ShowMenyAtRight");
  844. showMenyAtRightCheckbox.addEventListener("change", function() { setPlayerValue("ShowMenyAtRight", this.checked); location.reload(); }, false);
  845. }
  846. menuContent.style.minWidth = `${(maxClientWidth + 25)}px`;
  847. menuContent.style.display = "none";
  848. }
  849. }
  850. function getOrCreateAndResizeDropdown(branchIndex, baseElement, style, dropdownPosition = dropdownPositions.bottom, dropdownActivateMethod = dropdownActivateMethods.hover) {
  851. const dropdownId = `${baseElement.id}Dropdown`;
  852. let dropdown = document.getElementById(dropdownId);
  853. if(!dropdown) {
  854. const baseElementRect = baseElement.getBoundingClientRect();
  855. //console.log(baseElement.style);
  856. //console.log(getComputedStyle(baseElement));
  857. const borderBottomWidth = !isNaN(parseInt(baseElement.style.borderBottomWidth)) ? parseInt(baseElement.style.borderBottomWidth) : 0;
  858. let top = baseElementRect.height - borderBottomWidth;
  859. let left = 0;
  860. if(dropdownPosition == dropdownPositions.right) {
  861. top = 0;
  862. const borderRightWidth = !isNaN(parseInt(baseElement.style.borderRightWidth)) ? parseInt(baseElement.style.borderRightWidth) : 0;
  863. left = baseElementRect.width - borderRightWidth;
  864. }
  865. const dropdownName = `${baseElement.getAttribute("name")}Dropdown`;
  866. dropdown = addElement("div", { id: dropdownId, name: dropdownName, style: `position: absolute; box-shadow: 3px 3px 5px #333; top: ${top}px; left: ${left}px;` + (style || "") }, baseElement);
  867. if(dropdownActivateMethod == dropdownActivateMethods.hover) {
  868. baseElement.addEventListener("mouseenter", function() { dropdown.style.display = "block"; });
  869. baseElement.addEventListener("mouseleave", function() { dropdown.style.display = "none"; });
  870. baseElement.addEventListener("touchstart", function() { hideAllDropdown(dropdownName); dropdown.style.display = "block"; });
  871. } else {
  872. baseElement.addEventListener("click", function(e) {
  873. //console.log(e);
  874. e.stopPropagation();
  875. dropdown.style.display = dropdown.style.display == "none" ? "block" : "none";
  876. if(dropdown.style.display == "block") {
  877. [...document.querySelectorAll(`div[name='${dropdownName}']`)].filter(x => x.id != dropdownId).forEach(x => x.style.display = "none");
  878. }
  879. });
  880. }
  881. }
  882. return dropdown;
  883. }
  884. function hideAllDropdown(dropdownName) { Array.from(document.querySelectorAll(`div[name=${dropdownName}]`)).forEach(x => { x.style.display = "none"; }); }
  885. function createBuildButton() {
  886. if(!getPlayerBool("ShowAddSuperSetButton", true)) {
  887. return;
  888. }
  889. const fractionsMainMenuItem = document.getElementById(`SetsMenuItem${preferences.indexOf(fractionsPreferences)}`);
  890. let addBuildButton = document.getElementById("addBuildButton");
  891. if(addBuildButton) {
  892. return;
  893. }
  894. if(isNewInterface) {
  895. addBuildButton = addElement("div", { id: "addBuildButton", class: "position_tr", innerHTML: `<img class="NotificationIcon" src="${resourcesPath}/i/new_top/_panelBattles.png" style="width: 16px; height: 16px;" title="${isEn ? "Create build" : "Создать билд"}" >` }, fractionsMainMenuItem);
  896. } else {
  897. addBuildButton = addElement("a", { id: "addBuildButton", href: "javascript:void(0);", style: "text-decoration: none; vertical-align: bottom;", innerHTML: `<img src="${resourcesPath}/i/new_top/_panelBattles.png" style="width: 16px; height: 16px; border-radius: 50%;" title="${isEn ? "Create build" : "Создать билд"}" >` }, fractionsMainMenuItem);
  898. }
  899. addBuildButton.addEventListener("click", function(e) { e.stopPropagation(); addBuild(); });
  900. }
  901. function addBuild() {
  902. const superSetName = prompt(isEn ? "Enter build name" : "Введите название билда");
  903. if(superSetName) {
  904. //deletePlayerValue("SuperSets");
  905. const superSets = JSON.parse(getPlayerValue("SuperSets", "[]"));
  906. superSets.push({
  907. Id: Date.now(),
  908. Name: superSetName,
  909. Fraction: Fraction,
  910. WeaponSet: getPlayerValue(weaponSetsPreferences.getCurrentSetName(), -1),
  911. SkillSet: getPlayerValue(skillSetsPreferences.getCurrentSetName(), -1),
  912. ArmySet: getPlayerValue(armySetsPreferences.getCurrentSetName(), -1),
  913. MiniartsSet: Fraction == "3" ? getPlayerValue(miniartsPreferences.getCurrentSetName(), -1) : -1
  914. });
  915. setPlayerValue("SuperSets", JSON.stringify(superSets));
  916. }
  917. }
  918. function deleteBuild(id, menuItem) {
  919. const builds = JSON.parse(getPlayerValue("SuperSets", "[]"));
  920. const deletingIndex = builds.findIndex(x => x.Id == id);
  921. if(deletingIndex > -1) {
  922. builds.splice(deletingIndex, 1);
  923. setPlayerValue("SuperSets", JSON.stringify(builds));
  924. if(menuItem) {
  925. menuItem.remove();
  926. }
  927. }
  928. }
  929. async function applyBuild(superDropdownText, build) {
  930. const sets = [weaponSetsPreferences.name, skillSetsPreferences.name, armySetsPreferences.name];
  931. const originalText = superDropdownText ? superDropdownText.innerHTML : "";
  932. if(superDropdownText) {
  933. superDropdownText.innerHTML += " " + getWheelImage();
  934. }
  935. if(build.Fraction != Fraction) {
  936. await applySet(document.getElementById(`${fractionsPreferences.getCurrentSetName()}SetReference${build.Fraction}`), fractionsPreferences, fractionsPreferences.sets.find(x => x.number == build.Fraction), false);
  937. sets.push(fractionsPreferences.name);
  938. }
  939. await applySet(document.getElementById(`${weaponSetsPreferences.getCurrentSetName()}SetReference${build.WeaponSet}`), weaponSetsPreferences, weaponSetsPreferences.sets.find(x => x.number == build.WeaponSet), false);
  940. await applySet(document.getElementById(`${skillSetsPreferences.getCurrentSetName()}SetReference${build.SkillSet}`), skillSetsPreferences, skillSetsPreferences.sets.find(x => x.number == build.SkillSet), false);
  941. await applySet(document.getElementById(`${armySetsPreferences.getCurrentSetName()}SetReference${build.ArmySet}`), armySetsPreferences, armySetsPreferences.sets.find(x => x.number == build.ArmySet), false);
  942. if(build.Fraction == "3") {
  943. await applySet(document.getElementById(`${miniartsPreferences.getCurrentSetName()}SetReference${build.MiniartsSet}`), miniartsPreferences, miniartsPreferences.sets.find(x => x.number == build.MiniartsSet), false);
  944. }
  945. await updatePanels(sets);
  946. setPlayerValue("SuperSet", build.Id);
  947. if(superDropdownText) {
  948. Array.from(document.querySelectorAll("b[name=superDropdownText]")).forEach(x => x.style.color = "#f5c137");
  949. superDropdownText.innerHTML = originalText;
  950. superDropdownText.style.color = '#0f0';
  951. }
  952. }
  953. function markCurrent(selectedMenuItem, currentPreferences, currentSetNumber) {
  954. setPlayerValue(currentPreferences.getCurrentSetName(), currentSetNumber);
  955. if(selectedMenuItem) {
  956. selectedMenuItem.style.color = '#0f0';
  957. if(currentPreferences.currentMenuItem && currentPreferences.currentMenuItem != selectedMenuItem) {
  958. currentPreferences.currentMenuItem.style.color = "#f5c137";
  959. }
  960. currentPreferences.currentMenuItem = selectedMenuItem;
  961. }
  962. }
  963. function addSetChangerListener(htmlElement, currentPreferences, setNumber) {
  964. htmlElement.addEventListener("click", function() { markCurrent(currentPreferences.menuItems[setNumber], currentPreferences, setNumber); });
  965. }
  966. async function applySet(selectedMenuItem, currentPreferences, currentSet, callPageRefreshFunction = true) {
  967. markCurrent(selectedMenuItem, currentPreferences, currentSet.number);
  968. const originalText = selectedMenuItem ? selectedMenuItem.innerHTML : "";
  969. if(selectedMenuItem) {
  970. selectedMenuItem.innerHTML += " " + getWheelImage();
  971. }
  972. if(currentSet.url) {
  973. if(currentSet.method == "POST") {
  974. await postRequest(currentSet.url, currentSet.data);
  975. } else {
  976. await getRequest(currentSet.url);
  977. }
  978. }
  979. if(currentPreferences.applySet && currentPreferences.applySet.constructor.name === 'AsyncFunction') {
  980. await currentPreferences.applySet(currentSet);
  981. }
  982. if(selectedMenuItem) {
  983. selectedMenuItem.innerHTML = originalText;
  984. if(isMobileDevice) {
  985. selectedMenuItem.parentNode.parentNode.style.display = "none";
  986. }
  987. }
  988. const selectedValueDiv = document.getElementById(`hwmSetsMaster${currentPreferences.name}SelectedValue`);
  989. if(selectedValueDiv) {
  990. selectedValueDiv.innerHTML = currentSet.number;
  991. }
  992. if(typeof(currentPreferences.setChanged) == "function") {
  993. await currentPreferences.setChanged(currentSet.number);
  994. }
  995. if(callPageRefreshFunction) {
  996. updatePanels([currentPreferences.name]);
  997. }
  998. }
  999. function drowSkillChangers() {
  1000. if(location.pathname=='/home.php' && !document.querySelector(`#increaseattackAmountInput`)) {
  1001. let skillsCount = 0;
  1002. let re = new RegExp(isNewPersonPage ? `>${LocalizedString.AvailablePoints}:\\s(\\d+)<` : `<b>${LocalizedString.AvailablePoints}:</b>\\s(\\d+)`);
  1003. const skillsExec = re.exec(document.body.innerHTML);
  1004. if(skillsExec) {
  1005. skillsCount += parseInt(skillsExec[1]);
  1006. }
  1007. re = new RegExp(isNewPersonPage ? `>${LocalizedString.AvailableTalentPoints}:\\s(\\d+)<` : `<b>${LocalizedString.AvailableTalentPoints}:</b>\\s(\\d+)`);
  1008. const perksSkillsExec = re.exec(document.body.innerHTML);
  1009. if(perksSkillsExec) {
  1010. skillsCount += parseInt(perksSkillsExec[1]);
  1011. }
  1012. //console.log(`skillsCount: ${skillsCount}`);
  1013. if(skillsCount == 0) {
  1014. return;
  1015. }
  1016. const skillValueContainers = [];
  1017. if(isNewPersonPage) {
  1018. const container = document.querySelector("div#home_css_stats_wrap_div");
  1019. const inv_stat_dataDivs = container.querySelectorAll("div.inv_stat_data.home_stat_data.show_hint");
  1020. for(const inv_stat_dataDiv of inv_stat_dataDivs) {
  1021. const increaseButton = inv_stat_dataDiv.querySelector("div.home_button2.btn_hover2");
  1022. if(!increaseButton) {
  1023. continue;
  1024. }
  1025. const skillValueContainer = inv_stat_dataDiv.querySelector("div.inv_stat_text.home_stat_text");
  1026. skillValueContainers.push(skillValueContainer);
  1027. }
  1028. } else {
  1029. const increaseRefs = document.querySelectorAll("a[href^='home.php?increase=']"); //home.php?increase=defence
  1030. for(const increaseRef of increaseRefs) {
  1031. const sklilIncreaseCell = getParent(increaseRef, "td");
  1032. const skillValueContainer = sklilIncreaseCell.previousSibling;
  1033. skillValueContainers.push(skillValueContainer);
  1034. }
  1035. }
  1036. const skillNames = ["attack", "defence", "power", "knowledge"];
  1037. let i = 0;
  1038. for(const skillValueContainer of skillValueContainers) {
  1039. const skillValue = Number((skillValueContainer.querySelector("b") || skillValueContainer).innerText);
  1040. skillValueContainer.innerHTML = "";
  1041. const skill = skillNames[i];
  1042. const increaseAmountInput = addElement("input", { id: `increase${skill}AmountInput`, name: "increaseAmountInput", value: skillValue, type: "number", min: skillValue, max: skillValue + skillsCount, size: 4, onfocus: "this.select();", title: LocalizedString.IncreaseManyPointsTooltip.replace("${minValue}", skillValue + 1).replace("${maxValue}", skillValue + skillsCount) }, skillValueContainer);
  1043. increaseAmountInput.addEventListener("change", function() { const targetValue = Number(increaseAmountInput.value); if(targetValue > skillValue && targetValue <= skillValue + skillsCount) { changeSkill(increaseAmountInput, isNewPersonPage, skill, skillValue, targetValue); } });
  1044. i++;
  1045. }
  1046. }
  1047. }
  1048. async function changeSkill(increaseAmountInput, isNewPersonPage, skill, currentValue, targetValue) {
  1049. while(currentValue < targetValue) {
  1050. const url = `/home.php?increase=${skill}` + (isNewPersonPage ? `&info=1&js_output=1&rand=${Math.random() * 1000000}` : "");
  1051. const txt = await getRequestText(url, isNewPersonPage ? "text/html; charset=UTF-8" : "text/html; charset=windows-1251");
  1052. currentValue++;
  1053. if(isNewPersonPage) {
  1054. if (txt.substring(0, 7) != 'HCSS_OK') {
  1055. window.location = '/home.php?info';
  1056. return;
  1057. }
  1058. const data = txt.split('@');
  1059. const home_css_stats_wrap_div = document.getElementById('home_css_stats_wrap_div');
  1060. if(data && data[1] && home_css_stats_wrap_div) {
  1061. home_css_stats_wrap_div.innerHTML = data[1];
  1062. if(data.length > 2 && document.getElementById('home_css_mana_count')) {
  1063. document.getElementById('home_css_mana_count').innerHTML = parseInt(data[2]);
  1064. }
  1065. if(typeof win.hwm_hints_init === 'function') win.hwm_hints_init();
  1066. }
  1067. } else {
  1068. increaseAmountInput.value = currentValue;
  1069. }
  1070. }
  1071. if(!isNewPersonPage) {
  1072. location.reload();
  1073. }
  1074. }
  1075. function getWheelImage() { return `<img border="0" align="absmiddle" height="11" src="${resourcesPath}/css/loading.gif">`; }
  1076. function getFraction() {
  1077. let currentFractionNumber;
  1078. if(location.pathname == '/home.php') {
  1079. // for new home page
  1080. let currentFractionIconContainer = document.querySelector("div.home_css_pl_fract.show_hint");
  1081. if(!currentFractionIconContainer) {
  1082. currentFractionIconContainer = document.querySelector("a[href^='castle.php?change_faction_dialog']");
  1083. }
  1084. if(currentFractionIconContainer) {
  1085. const currentFractionIconImg = currentFractionIconContainer.querySelector("img");
  1086. currentFractionNumber = currentFractionIconImg.src.split("i/f/r")[1].split(".png")[0];
  1087. }
  1088. } else if(location.pathname=='/pl_info.php' && getUrlParamValue(location.href, "id") == PlayerId) {
  1089. const fractionImage = document.querySelector("img[src*='i/f/r']");
  1090. const regExp = new RegExp('\\/i\\/f\\/r(\\d+)\\.png');
  1091. const regExpExec = regExp.exec(fractionImage.src);
  1092. if(regExpExec) {
  1093. currentFractionNumber = regExpExec[1];
  1094. }
  1095. } else if(location.pathname=='/castle.php') {
  1096. const selectedFractionImg = document.querySelector("div.castle_faction_div_inside2 img");
  1097. const selectedFractionImgName = selectedFractionImg.getAttribute("src");
  1098. const selectedFractionNumber = selectedFractionImgName.split("kukla_png/kukla")[1].split(".")[0]; //dcdn.heroeswm.ru/i/kukla_png/kukla5.png
  1099.  
  1100. const fractionsDiv = document.querySelector("div[id='faction_list']");
  1101. if(fractionsDiv.getAttribute("style").includes("display:none;")) {
  1102. currentFractionNumber = selectedFractionNumber;
  1103. }
  1104. }
  1105. if(currentFractionNumber) {
  1106. setPlayerValue("Fraction", currentFractionNumber);
  1107. }
  1108. Fraction = parseInt(getPlayerValue("Fraction"));
  1109. }
  1110. async function updatePanels(sets) {
  1111. let pageReloadNeeded = false;
  1112. let panels = [];
  1113. if(sets.includes(weaponSetsPreferences.name) && ["/home.php", "/inventory.php", "/pl_info.php", "/map.php"].includes(location.pathname)) {
  1114. pageReloadNeeded = true;
  1115. if(location.pathname == '/home.php') {
  1116. pushNew(panels, homeArtsPanelSelector);
  1117. pushNew(panels, homeStatsPanelSelector);
  1118. pageReloadNeeded = false;
  1119. }
  1120. if(location.pathname == '/pl_info.php') {
  1121. if(getUrlParamValue(location.href, "id") == PlayerId) {
  1122. pushNew(panels, playerInfoArtsPanelSelector);
  1123. pushNew(panels, playerInfoStatsPanelSelector);
  1124. }
  1125. pageReloadNeeded = false;
  1126. }
  1127. if(location.pathname == '/map.php') {
  1128. pushNew(panels, mapHuntButtons2PanelSelector);
  1129. pushNew(panels, mapMercenaryTaskPanelSelector);
  1130. pageReloadNeeded = false;
  1131. }
  1132. }
  1133. if(sets.includes(skillSetsPreferences.name) && ["/skillwheel.php", "/pl_info.php", "/home.php", "/inventory.php"].includes(location.pathname)) {
  1134. pageReloadNeeded = true;
  1135. if(location.pathname == '/home.php') {
  1136. pushNew(panels, homeStatsPanelSelector);
  1137. pageReloadNeeded = false;
  1138. }
  1139. if(location.pathname == '/pl_info.php') {
  1140. if(getUrlParamValue(location.href, "id") == PlayerId) {
  1141. pushNew(panels, playerInfoStatsPanelSelector);
  1142. pushNew(panels, playerInfoPerksPanelSelector);
  1143. }
  1144. pageReloadNeeded = false;
  1145. }
  1146. if(location.pathname == '/inventory.php') {
  1147. pushNew(panels, inventoryStatsPanelSelector);
  1148. pageReloadNeeded = false;
  1149. }
  1150. }
  1151. if(sets.includes(armySetsPreferences.name) && ["/home.php", "/army.php", "/pl_info.php"].includes(location.pathname)) {
  1152. pageReloadNeeded = true;
  1153. if(location.pathname == '/home.php') {
  1154. pushNew(panels, homeArmyPanelSelector);
  1155. pageReloadNeeded = false;
  1156. }
  1157. if(location.pathname == '/pl_info.php') {
  1158. if(getUrlParamValue(location.href, "id") == PlayerId) {
  1159. pushNew(panels, playerInfoArmyPanelSelector);
  1160. }
  1161. pageReloadNeeded = false;
  1162. }
  1163. }
  1164. if(sets.includes(fractionsPreferences.name) && ["/home.php", "/army.php", "/pl_info.php", "/castle.php", "/inventory.php", "/skillwheel.php"].includes(location.pathname)) {
  1165. pageReloadNeeded = true;
  1166. }
  1167. if(sets.includes(miniartsPreferences.name) && ["/home.php", "/army.php", "/pl_info.php", "/magearts.php"].includes(location.pathname)) {
  1168. pageReloadNeeded = true;
  1169. if(location.pathname == '/home.php') {
  1170. pushNew(panels, homeArmyPanelSelector);
  1171. pageReloadNeeded = false;
  1172. }
  1173. if(location.pathname == '/pl_info.php') {
  1174. if(getUrlParamValue(location.href, "id") == PlayerId) {
  1175. pushNew(panels, playerInfoArmyPanelSelector);
  1176. }
  1177. pageReloadNeeded = false;
  1178. }
  1179. }
  1180. if(pageReloadNeeded) {
  1181. if(location.pathname == "/army.php") {
  1182. location = "/army.php";
  1183. } else {
  1184. location.reload();
  1185. }
  1186. } else {
  1187. await refreshUpdatePanels(panels);
  1188. }
  1189. }
  1190. function getWizardSkillLevel() {
  1191. if(location.pathname == "/home.php" || location.pathname == "/pl_info.php" && getUrlParamValue(location.href, "id") == PlayerId) {
  1192. if(isNewPersonPage) {
  1193. const wizardSkillLevelContainer = Array.from(document.querySelectorAll("div.home_inside_margins > div#row")).find(x => x.innerHTML.includes(isEn ? "Wizard" : "Маг"));
  1194. setPlayerValue("WizardSkillLevel", wizardSkillLevelContainer.querySelector("div#bartext > span").innerText);
  1195. } else {
  1196. const wizardSkillLevelExec = new RegExp(`${isEn ? "Wizard" : "Маг"}: (\\d+) \\(`).exec(document.body.innerHTML);
  1197. if(wizardSkillLevelExec) {
  1198. setPlayerValue("WizardSkillLevel", wizardSkillLevelExec[1]);
  1199. }
  1200. }
  1201. //console.log(`WizardSkillLevel: ${parseInt(getPlayerValue("WizardSkillLevel", 0))}`);
  1202. }
  1203. return parseInt(getPlayerValue("WizardSkillLevel", 0));
  1204. }
  1205.  
  1206. // API
  1207. function getPlayerFractionValue(key, defaultValue, fraction = Fraction) { return getPlayerValue(getFractionKey(key, fraction), defaultValue); };
  1208. function setPlayerFractionValue(key, value, fraction = Fraction) { setPlayerValue(getFractionKey(key, fraction), value); };
  1209. function deletePlayerFractionValue(key, fraction = Fraction) { return deletePlayerValue(getFractionKey(key, fraction)); };
  1210. function getFractionKey(key, fraction = Fraction) { return `${key}${fraction}f`; }