battle_damage_tooltip

Скрины с функционалом ниже. 1.) Показывает урон всех стеков одной стороны по одному выбранному стеку второй стороны. 2.) Если навести курсор на 1 существо, нажать 'e' (русская 'у'), сделать то же самое со вторым, то в чате появится урон первого по второму. Доп. выборы и настройка скорости анимации в настройках боя.

当前为 2023-06-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name battle_damage_tooltip
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.4.2
  5. // @description Скрины с функционалом ниже. 1.) Показывает урон всех стеков одной стороны по одному выбранному стеку второй стороны. 2.) Если навести курсор на 1 существо, нажать 'e' (русская 'у'), сделать то же самое со вторым, то в чате появится урон первого по второму. Доп. выборы и настройка скорости анимации в настройках боя.
  6. // @author Something begins
  7. // @license None
  8. // @match https://www.heroeswm.ru/war*
  9. // @match https://my.lordswm.com/war*
  10. // @match https://www.lordswm.com/war*
  11. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // @grant unsafeWindow
  15. // ==/UserScript==
  16. // Странные способы в некоторых местах обусловлены конфликтом со скриптом battleHelper от omne
  17. let outer_chat = document.getElementById("chat_format");
  18. let atLaunch = true
  19. outer_chat.insertAdjacentHTML("beforeend", `
  20. <div id="cre_distance_div" style="display: none"></div>
  21. <div id="individual_calc"></div>
  22. <div id="dmg_list_container"></div>
  23. <button id="dmg_list_refresh" style="background-color: #3d3d29; color: white; padding: 5px 10px; border: none; border-radius: 4px; font-size: 10px; cursor: pointer">Открыть</button>
  24. <select style="display : none; background-color: #333; color: white; margin: 10px" id="choose_cre"></select>
  25. <button id="change_side" style="background-color: #6b6b47; color: white; padding: 5px 10px; border: none; border-radius: 4px; font-size: 10px; cursor: pointer; display: none">Сменить сторону</button>
  26. <button id="collapse" style="background-color: #000000; color: white; padding: 5px 10px; border: none; border-radius: 4px; font-size: 10px; cursor: pointer; display: none; margin:10px">Свернуть</button> `)
  27.  
  28. let last_individual_calc = {}
  29. let isOpen = false
  30. let ini_weight = 10;
  31. let chosen = { side: 1, creature: "Высшие вампиры", afterSideSwitchCre: { "-1": "", "1": "" } }
  32. let chat = document.getElementById("chat_inside");
  33. let select = document.getElementById("choose_cre")
  34. let refresh_button = document.getElementById("dmg_list_refresh")
  35. let side_button = document.getElementById("change_side")
  36. let collapse_button = document.getElementById("collapse")
  37. let individual_calc = document.querySelector("#individual_calc")
  38. let cre_distance_div = document.querySelector("#cre_distance_div")
  39. const settings_panel = document.querySelector("#webgl_settings_whole")
  40. let dmg_list_container = document.querySelector("#dmg_list_container")
  41. cre_distance_div.innerHTML = `<span>Выбранное расстояние: ${GM_getValue('cre_distance')}</span><br>`
  42. outer_chat.insertAdjacentHTML('afterbegin', `<div id = "battlehelper_div"></div>`)
  43. let battlehelper_div = document.querySelector('#battlehelper_div');
  44.  
  45. // ========= utils ============
  46. function get_GM_value_if_exists(GM_key, default_value) {
  47. const GM_value = GM_getValue(GM_key)
  48. return GM_value != undefined ? GM_value : default_value;
  49. }
  50.  
  51. function getCurrentBattleSpeed() {
  52. for (let i = 1; i <= 3; i++) {
  53. let div = document.querySelector(`#speed${i}_button`)
  54. if (div.style.display === 'none') continue
  55. if (i === 1) return 2
  56. else if (i === 3) return 1
  57. else return 4
  58. }
  59. }
  60.  
  61. function setBattleSpeed(value) {
  62. if (value === 0) return value
  63. else if (value < 1) {
  64. unsafeWindow.timer_interval = Math.abs(value) * 20;
  65. return value
  66. }
  67. unsafeWindow.animspeed_def = unsafeWindow.animspeed = value
  68. unsafeWindow.animspeed_init = unsafeWindow.animspeed > 4 ? 0.5 : 2;
  69. unsafeWindow.timer_interval = Math.abs(value - 20);
  70. !unsafeWindow.timer_interval && unsafeWindow.timer_interval++;
  71. return value
  72. }
  73.  
  74. function individual_calc_innerHTML(atk_obj_index, def_obj_index) {
  75. if (atk_obj_index === undefined || def_obj_index === undefined) return ""
  76. let cre_collection = unsafeWindow.stage.pole.obj
  77. let attacker = cre_collection[atk_obj_index];
  78. let defender = cre_collection[def_obj_index];
  79. let dmg = get_dmg_info(atk_obj_index, def_obj_index)
  80. let distance_str = dmg.distance === "" ? "" : `<p> Прыжок на <u>${dmg.distance}</u> клеток </p>`
  81. last_individual_calc.atk_obj_index = atk_obj_index;
  82. last_individual_calc.def_obj_index = def_obj_index;
  83. individual_calc.style.backgroundColor = "#000066"
  84. return ` <div id="individual_cre_heading" style="display:inline; background-color: #000066">
  85. ${distance_str}
  86. <span>Урон <br>
  87. </span>
  88. <span>
  89. <b>${attacker.nametxt}</b> [${attacker.nownumber}] по <b>${defender.nametxt}</b> [${defender.nownumber}]: <br>
  90. <br>
  91. </span>
  92. </div>
  93. <p id="${0}" style=" background-color: #000066">
  94. <span style=color:#bfbfbf"></span>
  95. <b style="color:#ffffff; font-size: 120%; text-decoration: underline;">${dmg.min_killed}-${dmg.max_killed}</b> существ ( <span style="color:#ffffff">${dmg.min}-${dmg.max}</span>)
  96. </p>
  97. <br>`
  98. }
  99.  
  100. function paint_coords(x, y, color, timeout = 2077) {
  101. let tile = shado[x + y * defxn]
  102. if (tile == undefined) return
  103. tile.fill(color)
  104. set_visible(tile, 1)
  105.  
  106. setTimeout(() => {
  107. tile.fill(null)
  108. set_visible(tile, 0)
  109. }, timeout)
  110. }
  111. const observer = new MutationObserver((mutationsList, observer) => {
  112. for (const mutation of mutationsList) {
  113. if (mutation.type !== 'childList') return
  114. for (const addedNode of mutation.addedNodes) {
  115. if (!(addedNode.classList && addedNode.classList.contains('cont') || ["B", "BR"].includes(addedNode.tagName))) continue
  116. wrap_battlehelper();
  117. battlehelper_div.style.display = battleHelper_on ? "inline" : "none";
  118. }
  119. }
  120. });
  121.  
  122. function GM_toggle_boolean(GM_key, boolean) {
  123. boolean = !boolean
  124. GM_setValue(GM_key, boolean);
  125. return boolean
  126. }
  127.  
  128. function set_Display(element_arr, displayProperty) {
  129. element_arr.forEach(element => {
  130. if (element == null) return
  131. element.style.display = displayProperty
  132. })
  133. }
  134.  
  135. function readjust_elements() {
  136. chat = document.getElementById("chat_inside");
  137. select = document.getElementById("choose_cre")
  138. refresh_button = document.getElementById("dmg_list_refresh")
  139. side_button = document.getElementById("change_side")
  140. collapse_button = document.getElementById("collapse")
  141. dmg_list_container = document.querySelector("#dmg_list_container")
  142. individual_calc = document.querySelector("#individual_calc")
  143. cre_distance_div = document.querySelector("#cre_distance_div")
  144. battlehelper_div = document.querySelector("#battlehelper_div")
  145. }
  146. // -----------------------------------
  147. // ========= Настройки ============
  148.  
  149. let cre_distance = get_GM_value_if_exists('cre_distance', "")
  150. let cre_distance_on = get_GM_value_if_exists('cre_distance_on', false)
  151. let coeff_on = get_GM_value_if_exists('GM_coeff_on', false)
  152. let battleHelper_on = get_GM_value_if_exists('battleHelper_on', true)
  153. let animation_speed_on = get_GM_value_if_exists("animation_speed_on", false)
  154. const new_settings = `
  155. <style>
  156. .tooltip {
  157. position: relative;
  158. display: inline-block;
  159. text-size: 150%;
  160. color: brown;
  161. text-decoration: underline;
  162. }
  163.  
  164. .tooltip .tooltiptext {
  165. visibility: hidden;
  166. position: absolute;
  167. bottom: 100%;
  168. left: 50%;
  169. width: 5000%;
  170. transform: translateX(-30%);
  171. padding: 5px;
  172. background-color: #555;
  173. color: #fff;
  174. border-radius: 6px;
  175. word-wrap: break-word;
  176. }
  177.  
  178. .tooltip:hover .tooltiptext {
  179. visibility: visible;
  180. }
  181. </style>
  182. <div class="info_row">
  183. <label class="checkbox_container">коэф. урона <span class="tooltip">? <span class="tooltiptext">отношение урон/хп (т.е. у кого больше всех коэф., с того выгоднее начинать. <br>Работает в списке уронов если нажать на "Открыть" в чате)</span>
  184. </span>
  185. <input type="checkbox" checked="true" id="coeff_on">
  186. <span class="checkbox_checkmark"></span>
  187. </label>
  188. </div>
  189. <div class="info_row">
  190. <label class="checkbox_container">battleHelper в чате <span class="tooltip">? <span class="tooltiptext">выключает или включает начальное распределение АТБ шкалы от omne</span>
  191. </span> <input type="checkbox" checked="true" id="battleHelper_on">
  192. <span class="checkbox_checkmark"></span>
  193. </label>
  194. </div>
  195. <div class="info_row">
  196. <label class="checkbox_container">Расстояние между стеками <span class="tooltip">? <span class="tooltiptext">Расстояние между атакующим и защищающимся стеками. Выбирать расстояние стрелочками в текстовом поле снизу. <br>
  197. Влияет на статус урона стрелка (ближний/дальний урон, кривая/прямая стрела),
  198. разбег и прочие абилки, зависящие от расстояния. <br> Если выставить "Расстояние: 1", то стрелок будет считаться заблокированным.
  199. Если выставить расстояние больше 1,
  200. то стрелок будет считаться не заблокированным (даже если рядом с ним вражеское существо). </span>
  201. </span>
  202. <input type="checkbox" checked="true" id="cre_distance_on">
  203. <span class="checkbox_checkmark"></span>
  204. </label>
  205. <input type="number" style="width: 4%; margin: 2px 2px 2px 80px" id="cre_distance" onkeydown="return false;" value=${cre_distance}>
  206. </div>
  207. <div class="info_row">
  208. <label class="checkbox_container">Скорость анимации <span class="tooltip">? <span class="tooltiptext"> Скорость боевых анимаций. <br> Выбирать расстояние стрелочками в текстовом поле снизу или если зажать кнопку Alt и нажимать на стрелки клавиатуры.<br> Включить/выключить анимацию [Alt + P (русская З)].<br> Анимацию можно как ускорить, так и замедлить.<br>Верхний потолок у скорости 20, нижнего нету.<br> Негативный показатель означает скорость ниже возможной гвдшной. </span>
  209. </span>
  210. <input type="checkbox" checked="true" id="animation_speed_on">
  211. <span class="checkbox_checkmark"></span>
  212. </label>
  213. <input type="number" style="width: 5%; margin: 2px 2px 2px 80px" id="anim_speed" onkeydown="return false;" >
  214. </div>
  215.  
  216. `
  217. settings_panel.insertAdjacentHTML("beforeend", new_settings)
  218.  
  219. let settings_interval = setInterval(() => {
  220. if (Object.keys(unsafeWindow.stage.pole.obj).length !== 0) {
  221. document.querySelector("#coeff_on").checked = coeff_on
  222. document.querySelector("#battleHelper_on").checked = battleHelper_on
  223. document.querySelector("#cre_distance_on").checked = cre_distance_on
  224. document.querySelector("#animation_speed_on").checked = animation_speed_on
  225. let spd = get_GM_value_if_exists("anim_speed", getCurrentBattleSpeed())
  226. document.querySelector("#anim_speed").value = spd
  227. if (GM_getValue("animation_speed_on")) setBattleSpeed(spd)
  228. clearInterval(settings_interval)
  229. }
  230. }, 300)
  231. const distance_counter = document.getElementById("cre_distance");
  232. const anim_speed_counter = document.querySelector("#anim_speed")
  233.  
  234.  
  235. // ========= Event Listeners ============
  236. document.body.addEventListener('input', function(event) {
  237. switch (event.target.id) {
  238. case "cre_distance":
  239. if (distance_counter.value < 1) {
  240. distance_counter.value = 1
  241. return
  242. }
  243. if (!cre_distance_on) return
  244. GM_setValue('cre_distance', distance_counter.value)
  245. if (isOpen) refresh()
  246. individual_calc.innerHTML = individual_calc_innerHTML(last_individual_calc.atk_obj_index, last_individual_calc.def_obj_index)
  247. cre_distance_div.innerHTML = `<span>Выбранное расстояние: ${GM_getValue('cre_distance')}</span><br>`
  248. break;
  249. case "anim_speed":
  250. if (anim_speed_counter.value > 20) {
  251. anim_speed_counter.value = 20
  252. return
  253. }
  254. GM_setValue('anim_speed', anim_speed_counter.value)
  255. if (!animation_speed_on) return
  256. setBattleSpeed(anim_speed_counter.value)
  257. break;
  258. }
  259. });
  260. const anim_speed_input = document.querySelector('#anim_speed')
  261. document.addEventListener('keydown', event => {
  262. if (!event.altKey) return
  263. if (["P", "p", "з", "З"].includes(event.key)) {
  264. animation_speed_on = GM_toggle_boolean("animation_speed_on", GM_getValue("animation_speed_on"))
  265. document.querySelector("#animation_speed_on").checked = animation_speed_on
  266. if (animation_speed_on) {
  267. GM_setValue('anim_speed', anim_speed_counter.value)
  268. setBattleSpeed(anim_speed_counter.value);
  269. } else {
  270. setBattleSpeed(getCurrentBattleSpeed());
  271. }
  272. }
  273. if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
  274. event.preventDefault();
  275. const increment = event.key === 'ArrowUp' ? 1 : -1;
  276. anim_speed_input.value = parseInt(anim_speed_input.value) + increment;
  277. anim_speed_input.dispatchEvent(new Event('input', { bubbles: true }));
  278. }
  279. });
  280. document.body.addEventListener('change', function(event) {
  281. switch (event.target.id) {
  282. case "coeff_on":
  283. coeff_on = GM_toggle_boolean("coeff_on", coeff_on)
  284. if (isOpen) refresh()
  285. break;
  286. case "battleHelper_on":
  287. battleHelper_on = GM_toggle_boolean("battleHelper_on", battleHelper_on)
  288. battlehelper_div.style.display = battleHelper_on ? "inline" : "none";
  289. break
  290. case "choose_cre":
  291. chosen.creature = select.value
  292. refresh()
  293. break;
  294. case "cre_distance_on":
  295. cre_distance_on = GM_toggle_boolean("cre_distance_on", cre_distance_on)
  296. cre_distance_on ? GM_setValue('cre_distance', distance_counter.value) : GM_setValue('cre_distance', "")
  297. if (cre_distance_on) {
  298. cre_distance_div.innerHTML = `<span>Выбранное расстояние: ${GM_getValue('cre_distance')}</span><br>`
  299. cre_distance_div.style.display = "inline"
  300. if (isOpen) refresh()
  301. individual_calc.innerHTML = individual_calc_innerHTML(last_individual_calc.atk_obj_index, last_individual_calc.def_obj_index)
  302. } else {
  303. cre_distance_div.innerHTML = ""
  304. }
  305. if (isOpen) refresh()
  306. individual_calc.innerHTML = individual_calc_innerHTML(last_individual_calc.atk_obj_index, last_individual_calc.def_obj_index)
  307. break;
  308. case "animation_speed_on":
  309. animation_speed_on = GM_toggle_boolean("animation_speed_on", GM_getValue("animation_speed_on"))
  310. if (animation_speed_on) {
  311. GM_setValue('anim_speed', anim_speed_counter.value)
  312. setBattleSpeed(anim_speed_counter.value);
  313. } else {
  314. setBattleSpeed(getCurrentBattleSpeed());
  315. }
  316. break;
  317. }
  318. });
  319. document.body.addEventListener('click', function(event) {
  320. if (event.target.parentElement && /speed(.)_button/.test(event.target.parentElement.id)) {
  321. setBattleSpeed(getCurrentBattleSpeed());
  322. document.querySelector("#animation_speed_on").checked = false
  323. GM_setValue('animation_speed_on', false)
  324. }
  325. switch (event.target.id) {
  326. case "dmg_list_refresh":
  327. readjust_elements()
  328. refresh()
  329. break
  330. case "change_side":
  331. chosen.afterSideSwitchCre[chosen.side] = chosen.creature
  332. chosen.side = -chosen.side
  333. chosen.creature = chosen.afterSideSwitchCre[chosen.side]
  334. refresh()
  335. break
  336. case "collapse":
  337. readjust_elements()
  338. isOpen = false
  339. refresh_button.innerHTML = "Открыть";
  340. set_Display([select, side_button, collapse_button, document.querySelector("#chosen_cre_heading"), dmg_list_container, individual_calc, cre_distance_div], "none")
  341. break
  342. case "confirm_ins_img":
  343. observer.observe(outer_chat, { childList: true });
  344. setTimeout(function() {
  345. observer.disconnect();
  346. }, 190000);
  347. break;
  348. }
  349. });
  350.  
  351. function paint_two_coords() {
  352.  
  353. }
  354. // Урон одного стека по другому по выбору нажатием кнопки E
  355. let calc_x, calc_y, magshot_x, magshot_y;
  356. window.addEventListener("keyup", event => {
  357. /* // Должно быть открытие настроек на Esc, но чтоб не мешало в бою надо устранять ситуацию когда открыта инфа о существе и на esc она закрывается
  358. if (event.key === 'Escape'){
  359. for (const panel of document.querySelectorAll(".window_relative")){
  360. if (panel.style.display !== "none" && panel.id !== "win_Settings") {console.log("smth opened already"); return}
  361. }
  362. document.querySelector("#win_Settings").style.display = document.querySelector("#win_Settings").style.display ? '' : 'none';
  363. } */
  364. if ((document.querySelector("#chattext") !== document.activeElement) && (document.querySelector("#chattext_classic") !== document.activeElement)) {
  365. if (["e", "E", "у", "У"].includes(event.key)) {
  366. let cre_collection = unsafeWindow.stage.pole.obj
  367. if (mapobj[xr_last + yr_last * defxn] === undefined || cre_collection[mapobj[xr_last + yr_last * defxn]].rock === 1) {
  368. paint_coords(xr_last, yr_last, "#cccccc")
  369. return
  370. }
  371. if (!calc_x) {
  372. [calc_x, calc_y] = [xr_last, yr_last]
  373. paint_coords(xr_last, yr_last, "#800000")
  374. } else {
  375. readjust_elements()
  376. let atk_obj_index = unsafeWindow.mapobj[calc_x + calc_y * defxn]
  377. let def_obj_index = unsafeWindow.mapobj[xr_last + yr_last * defxn]
  378. set_Display([individual_calc, collapse_button], "inline")
  379. if (cre_distance_on) {
  380. cre_distance_div.style.display = "inline";
  381. cre_distance_div.innerHTML = `<span>Выбранное расстояние: ${GM_getValue('cre_distance')}</span><br>`
  382. }
  383. individual_calc.innerHTML = individual_calc_innerHTML(atk_obj_index, def_obj_index)
  384. calc_x = calc_y = null
  385. paint_coords(xr_last, yr_last, "blue")
  386. }
  387. }
  388. if (["u", "U", "г", "Г"].includes(event.key)) {
  389. if (!magshot_x) {
  390. [magshot_x, magshot_y] = [xr_last, yr_last]
  391. paint_coords(xr_last, yr_last, "#FC7052", 4000)
  392. } else {
  393. paint_coords(xr_last, yr_last, "#7A71FE", 4000)
  394. magshot(magshot_x, magshot_y, xr_last, yr_last)
  395. magshot_x = magshot_y = null
  396. }
  397. }
  398. }
  399. });
  400. // -----------------------------------
  401.  
  402. // ========= battleHelper ============
  403. function wrap_battlehelper() {
  404. for (const child of battlehelper_div.children) {
  405. if (child.className === "cont") return
  406. }
  407. readjust_elements();
  408. let el_transfer = [];
  409. [...outer_chat.children].forEach(child => {
  410. if (["B", "BR"].includes(child.tagName) || child.className == "cont") {
  411. el_transfer.push(child)
  412. }
  413. });
  414. for (const el of el_transfer) battlehelper_div.appendChild(el);
  415. const first_icons = battlehelper_div.querySelectorAll('br + div.cont');
  416. let teams_text = [...outer_chat.childNodes].filter(node => (node.nodeType === node.TEXT_NODE && /Команда №(\d)/.test(node.textContent.trim())));
  417. let i = 0
  418. for (const img_div of first_icons) {
  419. img_div.previousSibling.insertAdjacentHTML('beforebegin', teams_text[i].textContent);
  420. i++;
  421. }
  422. for (const el of teams_text) el.remove()
  423. let title_arr = [...battlehelper_div.children].filter(child => child.textContent === "Стартовый бонус АТБ")
  424. if (battlehelper_div.children.length === 2 && title_arr.length === 1) {
  425. battlehelper_div.innerHTML = " "
  426. }
  427. }
  428. observer.observe(outer_chat, { childList: true });
  429. setTimeout(function() {
  430. observer.disconnect();
  431. }, 30000);
  432.  
  433. // -----------------------------------
  434.  
  435.  
  436. // Функция рельсы гвд с поправкой на выбор клеток юзером
  437. function magshot(x1, y1, xr, yr) {
  438. var x2 = xr;
  439. var y2 = yr;
  440. var dx = Math.abs(x1 - x2);
  441. var dy = Math.abs(y1 - y2);
  442. var skip = false;
  443. if (x1 < x2) {
  444. var xp = 1;
  445. } else {
  446. var xp = -1;
  447. };
  448. if (y1 < y2) {
  449. var yp = 1;
  450. } else {
  451. var yp = -1;
  452. };
  453. if (dx > dy) {
  454. if (x1 > x2) {
  455. var x = -5;
  456. } else {
  457. var x = defxn + 3 - 1;
  458. };
  459. var y = (y2 - y1) / (x2 - x1) * (x - x1) + y1;
  460. } else {
  461. if (y1 > y2) {
  462. var y = -5;
  463. } else {
  464. var y = defyn + 5 + 1;
  465. };
  466. var x = (x2 - x1) / (y2 - y1) * (y - y1) + x1;
  467. };
  468. x = x1;
  469. y = y1;
  470. while ((x > 0) && (y > 0) && (x <= defxn - 2) && (y <= defyn)) {
  471. if (dx > dy) {
  472. x += xp;
  473. y = (y2 - y1) / (x2 - x1) * (x - x1) + y1;
  474. } else {
  475. y += yp;
  476. x = (x2 - x1) / (y2 - y1) * (y - y1) + x1;
  477. };
  478. let shot_coords = []
  479. if ((Math.round(x) > 0) && (Math.round(y) > 0) && (Math.round(x) <= defxn - 2) && (Math.round(y) <= defyn)) {
  480. if (shado[Math.round(x) + Math.round(y) * defxn]) {
  481. set_visible(shado[Math.round(x) + Math.round(y) * defxn], 1);
  482. shot_coords.push({ x: Math.round(x), y: Math.round(y) })
  483. }
  484. };
  485. setTimeout(() => {
  486. for (const coord of shot_coords) {
  487. set_visible(shado[coord.x + coord.y * defxn], 0);
  488. }
  489. }, 4000)
  490. };
  491. };
  492.  
  493. // Родная функция гвд с поправками на переменную l и модификаторами magic[]
  494. function attackmonster(attacker, ax, ay, x, y, defender, cre_distance, shootok, koef, inuse) {
  495. let cre_collection = unsafeWindow.stage.pole.obj
  496. var mainattack = 1;
  497. var ax1 = ax;
  498. var ay1 = ay;
  499. if (defender == 1000) return 0;
  500. if (defender <= 0) return 0;
  501. if (!cre_collection[defender]) return 0;
  502. if (cre_collection[defender]['hero']) return 0;
  503. if (cre_collection[defender]['rock']) return 0;
  504. if (koef == undefined) koef = 1;
  505. if (inuse == undefined) inuse = '';
  506. var len = unsafeWindow.wmap2[y * defxn + x];
  507. if ((cre_collection[attacker].x == x) && (cre_collection[attacker].y == y)) len = spd;
  508. shootok = 1;
  509.  
  510. function getAdjacentAndDiagonalCoords(x, y) {
  511. const adjacentAndDiagonalCoords = [];
  512. adjacentAndDiagonalCoords.push([x + 1, y]);
  513. adjacentAndDiagonalCoords.push([x - 1, y]);
  514. adjacentAndDiagonalCoords.push([x, y + 1]);
  515. adjacentAndDiagonalCoords.push([x, y - 1]);
  516. adjacentAndDiagonalCoords.push([x + 1, y + 1]);
  517. adjacentAndDiagonalCoords.push([x - 1, y + 1]);
  518. adjacentAndDiagonalCoords.push([x + 1, y - 1]);
  519. adjacentAndDiagonalCoords.push([x - 1, y - 1]);
  520. return adjacentAndDiagonalCoords;
  521. }
  522.  
  523. if (cre_collection[attacker].shots === 0) {
  524. shootok = 0
  525. } else {
  526. let attacker_adjacent_coords = getAdjacentAndDiagonalCoords(stage.pole.obj[attacker].x, stage.pole.obj[attacker].y)
  527. let enemies_list = Object.values(stage.pole.obj).filter(creature => creature.side != stage.pole.obj[attacker].side)
  528. enemies_list.forEach(enemy => {
  529. attacker_adjacent_coords.forEach(coord => {
  530. if (coord[0] == enemy.x && coord[1] == enemy.y) shootok = 0
  531. })
  532. })
  533. }
  534.  
  535. if (cre_collection[attacker]['big']) {
  536. if (ax > x) {
  537. x++;
  538. };
  539. if (ay > y) {
  540. y++;
  541. };
  542. };
  543. if (cre_collection[attacker]['bigx']) {
  544. if (ax > x) {
  545. x++;
  546. };
  547. };
  548. if (cre_collection[attacker]['bigy']) {
  549. if (ay > y) {
  550. y++;
  551. };
  552. };
  553. var spd = Math.max(0, Math.round((cre_collection[attacker].speed + cre_collection[attacker]['ragespeed'] + cre_collection[attacker]['speedaddon']) * cre_collection[attacker].speedmodifier));
  554. if (unsafeWindow.magic[attacker]['ent']) {
  555. spd = 0;
  556. };
  557. var movelen = spd - len;
  558. unsafeWindow.attacker_c = attacker;
  559. unsafeWindow.ax_c = ax;
  560. unsafeWindow.ay_c = ay;
  561. unsafeWindow.x_c = x;
  562. unsafeWindow.y_c = y;
  563. unsafeWindow.defender_c = defender;
  564. unsafeWindow.shootok_c = shootok;
  565. if ((x == 0) && (y == 0)) {
  566. x = cre_collection[attacker]['x'];
  567. y = cre_collection[attacker]['y'];
  568. };
  569. if ((defender > 0) && (cre_collection[defender]['big'])) {
  570. if ((x - ax > 1) && (ax < x) && (defender == mapobj[ay * defxn + ax + 1])) {
  571. ax++;
  572. };
  573. if ((y - ay > 1) && (ay < y) && (defender == mapobj[(ay + 1) * defxn + ax])) {
  574. ay++;
  575. };
  576. if ((ax - x > 1) && (ax > x) && (defender == mapobj[ay * defxn + ax - 1])) {
  577. ax--;
  578. };
  579. if ((ay - y > 1) && (ay > y) && (defender == mapobj[(ay - 1) * defxn + ax])) {
  580. ay--;
  581. };
  582. };
  583. if ((defender > 0) && (cre_collection[defender]['bigx'])) {
  584. if ((x - ax > 1) && (ax < x) && (defender == mapobj[ay * defxn + ax + 1])) {
  585. ax++;
  586. };
  587. if ((ax - x > 1) && (ax > x) && (defender == mapobj[ay * defxn + ax - 1])) {
  588. ax--;
  589. };
  590. };
  591. if ((defender > 0) && (cre_collection[defender]['bigy'])) {
  592. if ((y - ay > 1) && (ay < y) && (defender == mapobj[(ay + 1) * defxn + ax])) {
  593. ay++;
  594. };
  595. if ((ay - y > 1) && (ay > y) && (defender == mapobj[(ay - 1) * defxn + ax])) {
  596. ay--;
  597. };
  598. };
  599. let dx = x - ax;
  600. let dy = y - ay;
  601. l = dx * dx + dy * dy;
  602. if (movelen == undefined) movelen = 0;
  603. if (cre_distance !== "") {
  604. movelen = cre_distance
  605. l = Math.round(cre_distance * cre_distance)
  606. if (l > 2) shootok = 1
  607. else shootok = 0
  608. if (cre_collection[attacker].shots === 0) shootok = 0
  609. }
  610. unsafeWindow.PhysicalModifiers = 1;
  611. unsafeWindow.PhysicalModifiers *= koef;
  612. if (cre_collection[attacker]['shadowattack']) l = 0;
  613.  
  614. var hera = 0;
  615. var herd = 0;
  616. len = unsafeWindow.stage.pole.obj_array.length;
  617. for (var k1 = 0; k1 < len; k1++) {
  618. unsafeWindow.k = unsafeWindow.stage.pole.obj_array[k1];
  619.  
  620. if ((cre_collection[k].hero) && (cre_collection[k].owner == cre_collection[attacker].owner)) hera = unsafeWindow.k;
  621. if ((cre_collection[k].hero) && (cre_collection[k].owner == cre_collection[defender].owner)) herd = unsafeWindow.k;
  622. };
  623. if ((cre_collection[defender]['pirate']) && ((unsafeWindow.magic[defender]['sea']) || (unsafeWindow.gtype == 125) || (unsafeWindow.gtype == 126) || (unsafeWindow.gtype == 133))) {
  624. unsafeWindow.PhysicalModifiers *= 0.85;
  625. };
  626. if (cre_collection[defender]['deadflesh']) {
  627. unsafeWindow.PhysicalModifiers *= 0.8;
  628. };
  629. if (cre_collection[defender]['immaterial']) {
  630. unsafeWindow.PhysicalModifiers *= 0.65;
  631. };
  632. if ((cre_collection[attacker]['oppressionofweak']) && (cre_collection[defender]['level'] == 1)) {
  633. unsafeWindow.PhysicalModifiers *= 1.5;
  634. };
  635. if ((cre_collection[attacker]['fearofstrong']) && (cre_collection[defender]['level'] == 7)) {
  636. unsafeWindow.PhysicalModifiers *= 0.5;
  637. };
  638. if ((hera > 0) && (unsafeWindow.magic[hera]['bna'])) {
  639. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (1 + unsafeWindow.magic[hera]['bna']['effect'] / 100);
  640. if ((cre_collection[defender]['mechanical']) && (unsafeWindow.magic[hera]['MEC'])) {
  641. unsafeWindow.PhysicalModifiers *= 1 + unsafeWindow.magic[hera]['MEC']['effect'] / 100;
  642. };
  643. if ((cre_collection[attacker]['mechanical']) && (unsafeWindow.magic[hera]['mch'])) {
  644. unsafeWindow.PhysicalModifiers *= 1 + unsafeWindow.magic[hera]['mch']['effect'] / 100;
  645. };
  646. };
  647. if ((cre_collection[defender]['building']) && (!cre_collection[attacker]['siegewalls'])) {
  648. unsafeWindow.PhysicalModifiers *= 0.05;
  649. };
  650. if ((defender > 0) && (cre_collection[attacker]['cruelty']) && ((cre_collection[defender]['nowhealth'] < cre_collection[defender]['maxhealth']) || (cre_collection[defender]['nownumber'] < cre_collection[defender]['maxnumber']))) {
  651. unsafeWindow.PhysicalModifiers *= 1.15;
  652. };
  653. if ((defender > 0) && (cre_collection[attacker]['morecruelty']) && ((cre_collection[defender]['nowhealth'] < cre_collection[defender]['maxhealth']) || (cre_collection[defender]['nownumber'] < cre_collection[defender]['maxnumber']))) {
  654. unsafeWindow.PhysicalModifiers *= 1.3;
  655. };
  656. if ((cre_collection[attacker]['giantkiller']) && (cre_collection[defender]['big'])) unsafeWindow.PhysicalModifiers *= 2;
  657. if ((cre_collection[attacker]['pygmykiller']) && (!cre_collection[defender]['big'])) unsafeWindow.PhysicalModifiers *= 1.33;
  658. if (cre_collection[attacker]['stormstrike']) unsafeWindow.PhysicalModifiers *= 2;
  659. if ((cre_collection[attacker]['undeadkiller']) && (cre_collection[defender]['undead'])) unsafeWindow.PhysicalModifiers *= 1.5;
  660. if ((cre_collection[attacker]['pirate']) && (unsafeWindow.magic[defender]['blb'])) unsafeWindow.PhysicalModifiers *= 1.5;
  661. if ((!cre_collection[attacker]['hero']) && (unsafeWindow.magic[attacker]['zat'])) {
  662. unsafeWindow.PhysicalModifiers *= 1.15;
  663. };
  664. if ((herd > 0) && (unsafeWindow.magic[herd]['bnd'])) {
  665. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers / (1 + unsafeWindow.magic[herd]['bnd']['effect'] / 100);
  666. };
  667. if ((herd > 0) && (unsafeWindow.magic[herd]['fld'])) {
  668. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (1 - unsafeWindow.magic[herd]['fld']['effect'] / 100);
  669. };
  670. if ((herd > 0) && (unsafeWindow.magic[herd]['rcd']) && (monster_race[cre_collection[attacker]['id']] == unsafeWindow.magic[herd]['rcd']['effect'])) {
  671. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * 0.93;
  672. };
  673. if (unsafeWindow.magic[attacker]['prp']) {
  674. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (1 + unsafeWindow.magic[attacker]['prp']['effect'] / 100);
  675. };
  676. if (unsafeWindow.magic[defender]['sta']) {
  677. unsafeWindow.PhysicalModifiers *= 0.5;
  678. };
  679. if ((unsafeWindow.magic[attacker]['chd']) && (cre_collection[unsafeWindow.magic[attacker]['chd']['effect']]['nownumber'] > 0) && (unsafeWindow.magic[attacker]['chd']['effect'] != defender)) {
  680. unsafeWindow.PhysicalModifiers *= 0.55;
  681. };
  682. unsafeWindow.PhysicalModifiers *= unsafeWindow.stage.pole.checkmembrane(defender);
  683. if (!cre_collection[attacker]['hero']) {
  684. if ((l <= 2 || shootok === 0) && (cre_collection[attacker]['shooter']) && (!cre_collection[attacker]['nopenalty']) && (!cre_collection[attacker]['warmachine'])) {
  685. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * 0.5;
  686. };
  687. if ((l > 2) && (cre_collection[attacker]['rangepenalty'])) {
  688. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * 0.5;
  689. };
  690. unsafeWindow.rangemod = 1;
  691. if ((l > 2) && (cre_collection[attacker]['shooter']) && (((cre_collection[attacker]['range'] < Math.sqrt(l)) && (!cre_collection[attacker].shadowattack)) || ((iswalls) && (!cre_collection[attacker]['hero']) && (unsafeWindow.checkwall(x, y, ax, ay))))) {
  692. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * 0.5;
  693. unsafeWindow.rangemod = 0.5;
  694. };
  695. if ((l > 2) && (cre_collection[attacker]['shooter']) && (iswalls2) && (!cre_collection[attacker]['hero']) && (((!cre_collection[attacker].siegewalls) || (btype == 118)) || (!cre_collection[defender].stone)) && (unsafeWindow.checkwall2(x, y, ax, ay, attacker))) {
  696. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * 0.5;
  697. unsafeWindow.rangemod *= 0.5;
  698. };
  699. };
  700. var _PERK_ARCHERY = 11;
  701. var _PERK_EVASION = 22;
  702. if ((defender > 0) && (cre_collection[defender]['dodge']) && (((l <= 2) && (!cre_collection[attacker]['ballista']) && (inuse != 'ssh') && (inuse != 'mga') && (inuse != 'dcd') && (inuse != 'chs') && (!cre_collection[attacker]['hero'])) || (inuse == 'brs') || (inuse == 'cpt'))) {
  703. unsafeWindow.PhysicalModifiers *= 0.5;
  704. };
  705. if ((shootok === 1) && (!cre_collection[attacker]['hero']) && (cre_collection[attacker]['shooter'])) {
  706. if (unsafeWindow.isperk(attacker, _PERK_ARCHERY)) unsafeWindow.PhysicalModifiers *= 1.2;
  707. if (unsafeWindow.isperk(defender, _PERK_EVASION)) unsafeWindow.PhysicalModifiers *= 0.8;
  708. if ((!cre_collection[defender]['lshield']) && (unsafeWindow.stage.pole.shieldother(defender))) {
  709. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * 0.75;
  710. };
  711. if ((cre_collection[defender]['lshield']) || (cre_collection[defender]['hollowbones'])) {
  712. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * 0.5;
  713. };
  714. if (cre_collection[defender]['diamondarmor']) {
  715. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * 0.1;
  716. };
  717. if (cre_collection[defender]['shielded']) {
  718. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * 0.75;
  719. };
  720. if (cre_collection[defender]['unprotectedtarget']) {
  721. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * 1.25;
  722. };
  723. if (unsafeWindow.magic[defender]['dfm']) {
  724. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (1 - unsafeWindow.magic[defender]['dfm']['effect'] / 100);
  725. };
  726. if (unsafeWindow.magic[attacker]['cnf']) {
  727. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (1 - unsafeWindow.magic[attacker]['cnf']['effect'] / 100);
  728. };
  729.  
  730. if (hera > 0) {
  731. if (unsafeWindow.magic[hera]['sat']) {
  732. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (100 + unsafeWindow.magic[hera]['sat']['effect']) / 100;
  733. };
  734. };
  735. };
  736. if ((!cre_collection[attacker]['hero']) && (unsafeWindow.isperk(attacker, _PERK_BLESS))) {
  737. unsafeWindow.PhysicalModifiers *= 1.04;
  738. };
  739. let o = cre_collection[attacker]['owner'];
  740. if (unsafeWindow.magic[defender]['mf' + o]) {
  741. unsafeWindow.PhysicalModifiers *= 1 + unsafeWindow.magic[defender]['mf' + o]['effect'] / 100;
  742. };
  743. if ((!cre_collection[attacker]['hero']) && (unsafeWindow.isperk(attacker, _PERK_FERVOR))) {
  744. unsafeWindow.PhysicalModifiers *= 1.03;
  745. };
  746. if (hera > 0) {
  747. var h = hera;
  748. if ((unsafeWindow.magic[h]['nut']) && ((plid2 == -2) || (ohotnik_set_neutral()))) {
  749. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (100 + unsafeWindow.magic[h]['nut']['effect']) / 100;
  750. };
  751. if ((unsafeWindow.magic[h]['mle']) && ((cre_collection[attacker].shooter && shootok == 0) || (cre_collection[attacker].shooter != 1) || cre_collection[attacker].shots == 0)) {
  752. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (100 + unsafeWindow.magic[h]['mle']['effect']) / 100;
  753. };
  754. if (unsafeWindow.magic[attacker]['fbd']) {
  755. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (100 + Math.floor(unsafeWindow.magic[attacker]['fbd']['effect'] / 10)) / 100;
  756. };
  757. };
  758. let leap_atk_bonus, leap_distance = 0;
  759.  
  760. if (cre_collection[attacker].leap && l >= 4) {
  761. leap_distance = cre_distance ? cre_distance : Math.min((movelen - 1) * 2, Math.round(Math.sqrt(l)));
  762. leap_atk_bonus = cre_collection[attacker].attack * (1 + leap_distance * 0.1) - cre_collection[attacker].attack
  763. cre_collection[attacker]['attackaddon'] += leap_atk_bonus
  764. }
  765. unsafeWindow.monatt = cre_collection[attacker]['attack'] + cre_collection[attacker]['attackaddon'] + cre_collection[attacker]['rageattack'];
  766. if ((defender > 0) && (cre_collection[attacker]['giantslayer']) && (cre_collection[defender]['big'])) unsafeWindow.monatt += 4;
  767. if ((!cre_collection[attacker]['undead']) && (!cre_collection[attacker]['hero']) && (!cre_collection[attacker]['perseverance'])) {
  768. unsafeWindow.frig2 = false;
  769. unsafeWindow.i = attacker;
  770. var bigx = cre_collection[i]['big'];
  771. var bigy = cre_collection[i]['big'];
  772. if (cre_collection[i]['bigx']) bigx = 1;
  773. if (cre_collection[i]['bigy']) bigy = 1;
  774. unsafeWindow.xd = cre_collection[i]['x'];
  775. unsafeWindow.yd = cre_collection[i]['y'];
  776. for (var xz = unsafeWindow.xd - 1; xz <= unsafeWindow.xd + 1 + bigx; xz++) {
  777. for (var yz = unsafeWindow.yd - 1; yz <= unsafeWindow.yd + 1 + bigy; yz++) {
  778. if ((!unsafeWindow.frig2) && (mapobj[yz * defxn + xz] > 0) && (cre_collection[mapobj[yz * defxn + xz]]['side'] != cre_collection[i]['side']) && (cre_collection[mapobj[yz * defxn + xz]]['festeringaura']) && (cre_collection[mapobj[yz * defxn + xz]]['nownumber'] > 0)) {
  779. unsafeWindow.monatt -= 4;
  780. unsafeWindow.frig2 = true;
  781. };
  782. };
  783. };
  784. };
  785.  
  786. if ((unsafeWindow.magic[attacker]['bsr']) || (unsafeWindow.magic[attacker]['rof'])) {
  787. unsafeWindow.monatt += Math.floor((cre_collection[attacker]['defence'] + cre_collection[attacker]['defenceaddon'] + cre_collection[attacker]['ragedefence']) * cre_collection[attacker]['defencemodifier']);
  788. };
  789. if (herd > 0) {
  790. h = herd;
  791. if ((unsafeWindow.magic[h]['mld']) && ((cre_collection[attacker].shooter && shootok == 0) || (cre_collection[attacker].shooter != 1) || cre_collection[attacker].shots == 0)) {
  792. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (100 - unsafeWindow.magic[h]['mld']['effect']) / 100;
  793. };
  794. if ((unsafeWindow.magic[h]['_ia']) && (!cre_collection[attacker]['perseverance'])) {
  795. unsafeWindow.monatt *= (1 - unsafeWindow.magic[h]['_ia']['effect'] / 100);
  796. };
  797. if ((!cre_collection[attacker]['hero']) && (cre_collection[attacker].shooter) && (cre_collection[attacker].shots != 0) && (unsafeWindow.magic[h]['msk']) && shootok == 1) {
  798. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (100 - unsafeWindow.magic[h]['msk']['effect']) / 100;
  799. };
  800. };
  801. unsafeWindow.defadd = 0;
  802. if (cre_collection[defender]['agility']) {
  803. if (!unsafeWindow.magic[defender]['agl']) unsafeWindow.defadd = cre_collection[defender]['speed'] * 2;
  804. };
  805. if ((cre_collection[defender]['spirit']) && (!unsafeWindow.magic[defender]['spi'])) {
  806. unsafeWindow.PhysicalModifiers *= 0.5;
  807. };
  808. if ((cre_collection[attacker]['rageagainsttheliving']) && (cre_collection[defender]['alive'])) {
  809. unsafeWindow.PhysicalModifiers *= 1.3;
  810. };
  811. if ((cre_collection[defender]['defensivestance']) && (!unsafeWindow.magic[defender]['mvd'])) {
  812. unsafeWindow.defadd += 5;
  813. };
  814. if ((!cre_collection[defender]['undead']) && (!cre_collection[defender]['armoured']) && (!cre_collection[defender]['organicarmor'])) {
  815. unsafeWindow.frig2 = false;
  816. unsafeWindow.i = defender;
  817. bigx = cre_collection[i]['big'];
  818. bigy = cre_collection[i]['big'];
  819. if (cre_collection[i]['bigx']) bigx = 1;
  820. if (cre_collection[i]['bigy']) bigy = 1;
  821. unsafeWindow.xd = cre_collection[i]['x'];
  822. unsafeWindow.yd = cre_collection[i]['y'];
  823. for (let xz = unsafeWindow.xd - 1; xz <= unsafeWindow.xd + 1 + bigx; xz++) {
  824. for (let yz = unsafeWindow.yd - 1; yz <= unsafeWindow.yd + 1 + bigy; yz++) {
  825. if ((!unsafeWindow.frig2) && (mapobj[yz * defxn + xz] > 0) && (cre_collection[mapobj[yz * defxn + xz]]['side'] != cre_collection[i]['side']) && (cre_collection[mapobj[yz * defxn + xz]]['festeringaura']) && (cre_collection[mapobj[yz * defxn + xz]]['nownumber'] > 0)) {
  826. unsafeWindow.defadd -= 4;
  827. unsafeWindow.frig2 = true;
  828. };
  829. };
  830. };
  831. };
  832. if ((attacker > 0) && (cre_collection[defender]['giantslayer']) && (cre_collection[attacker]['big'])) unsafeWindow.defadd += 4;
  833. unsafeWindow.mondef = Math.round((cre_collection[defender]['defence'] + cre_collection[defender]['defenceaddon'] + unsafeWindow.defadd + cre_collection[defender]['ragedefence']) * cre_collection[defender]['defencemodifier']);
  834. if (unsafeWindow.magic[defender]['bsr']) {
  835. unsafeWindow.mondef = 0;
  836. };
  837.  
  838. if ((cre_collection[attacker]['preciseshot']) && (l > 2) && (l <= 9) && (unsafeWindow.rangemod >= 1)) {
  839. unsafeWindow.mondef = 0;
  840. };
  841. if ((cre_collection[attacker]['ignoredefence'])) {
  842. unsafeWindow.mondef *= (1 - cre_collection[attacker]['ignoredefence'] / 100);
  843. };
  844. if (cre_collection[attacker]['crushingleadership']) {
  845. var morale_delta = unsafeWindow.stage.pole.getmorale(attacker) - unsafeWindow.stage.pole.getmorale(defender);
  846. if (morale_delta > 0) {
  847. unsafeWindow.mondef *= Math.max(0, 1 - morale_delta / 10);
  848. };
  849. };
  850. if (cre_collection[attacker]['sacredweapon']) {
  851. var dark_count = get_dark_count(defender);
  852. if (dark_count > 0) {
  853. unsafeWindow.mondef *= Math.max(0, 1 - 0.15 * dark_count);
  854. };
  855. };
  856. if (unsafeWindow.isperk(attacker, _PERK_PIERCING_LUCK)) {
  857. unsafeWindow.mondef *= 1 - Math.max(0, 0.025 * (cre_collection[attacker]['luck'] + cre_collection[attacker]['luckaddon']));
  858. };
  859. if ((cre_collection[defender]['ignoreattack'])) {
  860. unsafeWindow.monatt *= (1 - cre_collection[defender]['ignoreattack'] / 100);
  861. };
  862. if ((cre_collection[attacker]['ridercharge']) && (movelen > 0)) {
  863. unsafeWindow.mondef = unsafeWindow.mondef * (5 - movelen) / 5;
  864. };
  865. if ((cre_collection[attacker]['forcearrow']) && (!cre_collection[defender]['armoured']) && (!cre_collection[defender]['organicarmor']) && (l > 2)) {
  866. unsafeWindow.mondef *= 0.8;
  867. };
  868. if ((cre_collection[attacker]['armorpiercing']) && (!cre_collection[defender]['armoured']) && (!cre_collection[defender]['organicarmor']) && (l > 2)) {
  869. unsafeWindow.mondef *= 0.5;
  870. };
  871. if ((cre_collection[attacker]['jousting']) && (movelen > 0)) {
  872. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (1 + 0.05 * movelen);
  873. };
  874. if (((cre_collection[attacker]['blindingcharge']) || (cre_collection[attacker]['charge'])) && (movelen > 0)) {
  875. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * (1 + 0.1 * movelen);
  876. };
  877. if ((cre_collection[defender]['shieldwall']) && (movelen > 0)) {
  878. unsafeWindow.PhysicalModifiers = unsafeWindow.PhysicalModifiers * Math.max(0.1, 1 - 0.1 * movelen);
  879. };
  880. if ((unsafeWindow.magic[defender]['enc']) && (unsafeWindow.magic[defender]['enc']['effect'] == 1)) {
  881. unsafeWindow.PhysicalModifiers *= 0.5;
  882. };
  883. if ((cre_collection[attacker]['safeposition']) && (movelen == 0)) {
  884. unsafeWindow.PhysicalModifiers *= 1.5;
  885. };
  886. if ((cre_collection[attacker]['agilesteed']) && (movelen > 0)) {
  887. unsafeWindow.PhysicalModifiers *= 1 - 0.05 * movelen;
  888. };
  889. if (unsafeWindow.mondef < 0) {
  890. unsafeWindow.mondef = 0;
  891. };
  892.  
  893. unsafeWindow.air = 0;
  894. unsafeWindow.fire = 0;
  895. unsafeWindow.water = 0;
  896. unsafeWindow.earth = 0;
  897. if ((hera > 0) && (!cre_collection[attacker]['taran'])) {
  898. h = hera;
  899. if (unsafeWindow.magic[h]['_id']) {
  900. unsafeWindow.mondef *= (1 - unsafeWindow.magic[h]['_id']['effect'] / 100);
  901. };
  902. if (unsafeWindow.magic[h]['_aa']) {
  903. unsafeWindow.air = unsafeWindow.magic[h]['_aa']['effect'] / 100;
  904. };
  905. if (unsafeWindow.magic[h]['_af']) {
  906. unsafeWindow.fire = unsafeWindow.magic[h]['_af']['effect'] / 100;
  907. };
  908. if (unsafeWindow.magic[h]['_aw']) {
  909. unsafeWindow.water = unsafeWindow.magic[h]['_aw']['effect'] / 100;
  910. };
  911. if (unsafeWindow.magic[h]['_ae']) {
  912. unsafeWindow.earth = unsafeWindow.magic[h]['_ae']['effect'] / 100;
  913. };
  914. };
  915. if ((cre_collection[defender]['armoured']) || (cre_collection[defender]['organicarmor'])) {
  916. unsafeWindow.mondef = Math.round((cre_collection[defender]['defence'] + cre_collection[defender]['defenceaddon'] + cre_collection[defender]['ragedefence']) * cre_collection[defender]['defencemodifier']);
  917. };
  918. if (unsafeWindow.monatt < 0) {
  919. unsafeWindow.monatt = 0;
  920. };
  921. if (unsafeWindow.monatt > unsafeWindow.mondef) {
  922. unsafeWindow.AttackDefenseModifier = 1 + (unsafeWindow.monatt - unsafeWindow.mondef) * 0.05;
  923. } else {
  924. unsafeWindow.AttackDefenseModifier = 1 / (1 + (unsafeWindow.mondef - unsafeWindow.monatt) * 0.05);
  925. };
  926. if (cre_collection[attacker]['hero']) {
  927. unsafeWindow.AttackDefenseModifier = 1;
  928. };
  929. var _PERK_ATTACK1 = 8;
  930. var _PERK_ATTACK2 = 9;
  931. var _PERK_ATTACK3 = 10;
  932. var _PERK_DEFENSE1 = 19;
  933. var _PERK_DEFENSE2 = 20;
  934. var _PERK_DEFENSE3 = 21;
  935.  
  936. if ((!cre_collection[attacker]['hero']) && ((cre_collection[attacker].shooter && shootok == 0) || (cre_collection[attacker].shooter != 1))) {
  937. if (unsafeWindow.isperk(attacker, _PERK_ATTACK3)) {
  938. unsafeWindow.PhysicalModifiers *= 1.3;
  939. } else {
  940. if (unsafeWindow.isperk(attacker, _PERK_ATTACK2)) {
  941. unsafeWindow.PhysicalModifiers *= 1.2;
  942. } else
  943. if (unsafeWindow.isperk(attacker, _PERK_ATTACK1)) unsafeWindow.PhysicalModifiers *= 1.1;
  944. };
  945. if (unsafeWindow.isperk(defender, _PERK_DEFENSE3)) {
  946. unsafeWindow.PhysicalModifiers *= 0.7;
  947. } else {
  948. if (unsafeWindow.isperk(defender, _PERK_DEFENSE2)) {
  949. unsafeWindow.PhysicalModifiers *= 0.8;
  950. } else {
  951. if (unsafeWindow.isperk(defender, _PERK_DEFENSE1)) unsafeWindow.PhysicalModifiers *= 0.9;
  952. };
  953. };
  954. };
  955. if ((cre_collection[attacker]['siegewalls']) && (cre_collection[defender]['stone'])) {
  956. unsafeWindow.PhysicalModifiers *= 10;
  957. };
  958. var _PERK_COLD_STEEL = 14;
  959. var _PERK_FIERY_WRATH = 101;
  960. var _PERK_HELLFIRE_AURA = 123;
  961. var _PERK_RETRIBUTION = 16;
  962.  
  963. if (unsafeWindow.isperk(attacker, _PERK_COLD_STEEL)) unsafeWindow.water = 1 - (1 - unsafeWindow.water) * (0.9);
  964. if (unsafeWindow.isperk(attacker, _PERK_FIERY_WRATH)) unsafeWindow.fire = 1 - (1 - unsafeWindow.fire) * (0.85);
  965. if (unsafeWindow.isperk(attacker, _PERK_HELLFIRE_AURA)) unsafeWindow.fire = 1 - (1 - unsafeWindow.fire) * (0.95);
  966.  
  967. if (unsafeWindow.magic[attacker]['cre']) {
  968. unsafeWindow.air = 1 - (1 - unsafeWindow.air) * (1 - unsafeWindow.magic[attacker]['cre']['effect'] / 100);
  969. };
  970.  
  971. if (unsafeWindow.isperk(attacker, _PERK_RETRIBUTION)) unsafeWindow.PhysicalModifiers *= (1 + Math.min(Math.max(unsafeWindow.stage.pole.getmorale(attacker, x, y), 0), 5) / 20);
  972. if ((cre_collection[attacker]['viciousstrike']) && (Math.max(0, Math.round((cre_collection[defender]['speed'] + cre_collection[defender]['ragespeed'] + cre_collection[defender]['speedaddon']) * cre_collection[defender]['speedmodifier'])) == 0)) unsafeWindow.PhysicalModifiers *= 1.5;
  973. unsafeWindow.PhysicalModifiers *= unsafeWindow.stage.pole.magicmod(attacker, defender, unsafeWindow.fire, unsafeWindow.air, unsafeWindow.water, unsafeWindow.earth, 0.1);
  974. if ((cre_collection[attacker]['bloodfrenzy']) && (unsafeWindow.magic[defender]['fd1'])) {
  975. unsafeWindow.PhysicalModifiers *= 1.3;
  976. };
  977. unsafeWindow.UmelkaModifiers = 1;
  978.  
  979. if ((umelka[cre_collection[attacker]['owner']][0] > 0) && (umelka[cre_collection[defender]['owner']][0] > 0)) {
  980. unsafeWindow.k = umelka[cre_collection[attacker]['owner']][0];
  981. if ((unsafeWindow.k > 0) && (unsafeWindow.k < 11)) {
  982. let j = umelka[cre_collection[defender]['owner']][k];
  983. unsafeWindow.UmelkaModifiers = 1 - j * 0.03;
  984. };
  985. };
  986. unsafeWindow.NumCreatures = cre_collection[attacker]['nownumber'];
  987. let tsc = 0;
  988.  
  989. bigx = cre_collection[defender]['big'];
  990. bigy = cre_collection[defender]['big'];
  991. if (cre_collection[defender]['bigx']) bigx = 1;
  992. if (cre_collection[defender]['bigy']) bigy = 1;
  993. for (var xs = cre_collection[defender]['x'] - 1; xs <= cre_collection[defender]['x'] + 1 + bigx; xs++) {
  994. for (var ys = cre_collection[defender]['y'] - 1; ys <= cre_collection[defender]['y'] + 1 + bigy; ys++) {
  995. if ((mapobj[xs + ys * defxn] > 0) && (mapobj[xs + ys * defxn] != defender) && (cre_collection[mapobj[xs + ys * defxn]]['shieldguard']) && (cre_collection[defender]['side'] == cre_collection[mapobj[xs + ys * defxn]]['side'])) {
  996. tsc++;
  997. };
  998. };
  999. };
  1000.  
  1001.  
  1002. unsafeWindow.PhysicalModifiers /= (tsc + 1);
  1003.  
  1004. var minmag = 0;
  1005. var maxmag = 0;
  1006. if ((inuse == 'lep') && (cre_collection[attacker]['crashingleap'])) {
  1007. unsafeWindow.Totalmagicdamage = 0;
  1008. cre_collection[defender]['attacked'] = 1;
  1009. unsafeWindow.stage.pole.attackmagic(attacker, defender, cre_collection[attacker]['nownumber'] * 4, 'cold', '', 0, 0, 0);
  1010. minmag = unsafeWindow.Totalmagicdamage;
  1011. unsafeWindow.Totalmagicdamage = 0;
  1012. cre_collection[defender]['attacked'] = 1;
  1013. unsafeWindow.stage.pole.attackmagic(attacker, defender, cre_collection[attacker]['nownumber'] * 6, 'cold', '', 0, 0, 0);
  1014. maxmag = unsafeWindow.Totalmagicdamage;
  1015. };
  1016.  
  1017. unsafeWindow.mindam = cre_collection[attacker]['mindam'] + cre_collection[attacker]['damageaddon'] + (cre_collection[attacker]['maxdam'] - cre_collection[attacker]['mindam']) * (cre_collection[attacker]['mindamaddon']) + cre_collection[attacker]['ragedamage'];
  1018. unsafeWindow.maxdam = cre_collection[attacker]['maxdam'] + cre_collection[attacker]['damageaddon'] - (cre_collection[attacker]['maxdam'] - cre_collection[attacker]['mindam']) * (cre_collection[attacker]['maxdamaddon']) + cre_collection[attacker]['ragedamage'];
  1019. h = hera;
  1020. if ((h > 0) && (unsafeWindow.magic[h]) && (unsafeWindow.magic[h]['BLS']) && (unsafeWindow.magic[h]['BLS']['effect'] > 0)) unsafeWindow.mindam = unsafeWindow.maxdam;
  1021. if ((h > 0) && (unsafeWindow.magic[h]) && (unsafeWindow.magic[h]['CRS']) && (unsafeWindow.magic[h]['CRS']['effect'] > 0)) unsafeWindow.maxdam = unsafeWindow.mindam;
  1022. if ((cre_collection[attacker]['taran']) && (cre_collection[defender]['stone'])) {
  1023. h = hera;
  1024. unsafeWindow.mindam = Math.floor(Math.pow(cre_collection[h]['maxhealth'], 0.5) * 200 * cre_collection[attacker]['mindam']);
  1025. unsafeWindow.maxdam = Math.floor(Math.pow(cre_collection[h]['maxhealth'], 0.5) * 400 * cre_collection[attacker]['maxdam']);
  1026. };
  1027. if (cre_collection[attacker]['accuracy']) unsafeWindow.mindam = unsafeWindow.maxdam;
  1028. unsafeWindow.BaseDamage = unsafeWindow.mindam;
  1029. unsafeWindow.PhysicalDamage = unsafeWindow.NumCreatures * unsafeWindow.BaseDamage * unsafeWindow.AttackDefenseModifier * unsafeWindow.PhysicalModifiers * unsafeWindow.UmelkaModifiers + minmag;
  1030. unsafeWindow.PhysicalDamage2 = unsafeWindow.NumCreatures * unsafeWindow.maxdam * unsafeWindow.AttackDefenseModifier * unsafeWindow.PhysicalModifiers * unsafeWindow.UmelkaModifiers + maxmag;
  1031. if ((cre_collection[attacker]['deathstrike']) && (cre_collection[defender]['maxhealth'] < 400) && (!cre_collection[defender]['stone'])) {
  1032. if ((cre_collection[defender]['nownumber'] - 1) * cre_collection[defender]['maxhealth'] + cre_collection[defender]['nowhealth'] > unsafeWindow.PhysicalDamage) {
  1033. unsafeWindow.PhysicalDamage += cre_collection[defender]['maxhealth'] - unsafeWindow.PhysicalDamage % cre_collection[defender]['maxhealth'];
  1034. };
  1035. if ((cre_collection[defender]['nownumber'] - 1) * cre_collection[defender]['maxhealth'] + cre_collection[defender]['nowhealth'] > unsafeWindow.PhysicalDamage2) {
  1036. unsafeWindow.PhysicalDamage2 += cre_collection[defender]['maxhealth'] - unsafeWindow.PhysicalDamage2 % cre_collection[defender]['maxhealth'];
  1037. };
  1038. };
  1039.  
  1040. if (cre_collection[attacker]['bladeofslaughter']) {
  1041. unsafeWindow.PhysicalDamage += Math.min(500, cre_collection[defender]['nownumber'] * 2);
  1042. unsafeWindow.PhysicalDamage2 += Math.min(500, cre_collection[defender]['nownumber'] * 2);
  1043. };
  1044. if (unsafeWindow.magic[attacker]['brk']) {
  1045. unsafeWindow.PhysicalDamage *= (1 + unsafeWindow.magic[attacker]['brk']['effect'] * 0.03);
  1046. unsafeWindow.PhysicalDamage2 *= (1 + unsafeWindow.magic[attacker]['brk']['effect'] * 0.03);
  1047. };
  1048. if (unsafeWindow.PhysicalDamage < 1) {
  1049. unsafeWindow.PhysicalDamage = 1;
  1050. };
  1051. if (unsafeWindow.PhysicalDamage2 < 1) {
  1052. unsafeWindow.PhysicalDamage2 = 1;
  1053. };
  1054. if ((cre_collection[attacker]['magicattack']) && (unsafeWindow.l > 2) && (unsafeWindow.stage.pole.issomething(defender, 'dampenmagic'))) unsafeWindow.PhysicalDamage = 0;
  1055. if (unsafeWindow.magic[defender]['rag']) {
  1056. unsafeWindow.PhysicalDamage = unsafeWindow.stage.pole.ragedamage(defender, unsafeWindow.PhysicalDamage);
  1057. unsafeWindow.PhysicalDamage2 = unsafeWindow.stage.pole.ragedamage(defender, unsafeWindow.PhysicalDamage2);
  1058. };
  1059. if ((cre_collection[attacker]['vorpalsword']) && (cre_collection[defender]['maxhealth'] < 400) && (!cre_collection[defender]['stone'])) {
  1060. unsafeWindow.PhysicalDamage += cre_collection[defender]['maxhealth'];
  1061. unsafeWindow.PhysicalDamage2 += cre_collection[defender]['maxhealth'];
  1062. };
  1063.  
  1064. unsafeWindow.PhysicalDamage = Math.round(unsafeWindow.PhysicalDamage);
  1065. unsafeWindow.PhysicalDamage2 = Math.round(unsafeWindow.PhysicalDamage2);
  1066. if (cre_collection[defender]['pleasureinpain']) {
  1067. unsafeWindow.PhysicalDamage = Math.round(unsafeWindow.PhysicalDamage * 0.9);
  1068. unsafeWindow.PhysicalDamage2 = Math.round(unsafeWindow.PhysicalDamage2 * 0.9);
  1069. };
  1070. if (cre_collection[defender]['raptureinagony']) {
  1071. unsafeWindow.PhysicalDamage = Math.round(unsafeWindow.PhysicalDamage * 0.8);
  1072. unsafeWindow.PhysicalDamage2 = Math.round(unsafeWindow.PhysicalDamage2 * 0.8);
  1073. };
  1074. var totalh = (cre_collection[defender]['nownumber'] - 1) * cre_collection[defender]['maxhealth'] + cre_collection[defender]['nowhealth'];
  1075. unsafeWindow.Uronkills = Math.floor(Math.min(unsafeWindow.PhysicalDamage, totalh) / cre_collection[defender]['maxhealth']);
  1076. unsafeWindow.Uronkills2 = Math.floor(Math.min(unsafeWindow.PhysicalDamage2, totalh) / cre_collection[defender]['maxhealth']);
  1077. var nowhealth = cre_collection[defender]['nowhealth'] - (Math.min(unsafeWindow.PhysicalDamage, totalh) - unsafeWindow.Uronkills * cre_collection[defender]['maxhealth']);
  1078. var nowhealth2 = cre_collection[defender]['nowhealth'] - (Math.min(unsafeWindow.PhysicalDamage2, totalh) - unsafeWindow.Uronkills2 * cre_collection[defender]['maxhealth']);
  1079. if (nowhealth <= 0) unsafeWindow.Uronkills++;
  1080. if (nowhealth2 <= 0) unsafeWindow.Uronkills2++;
  1081. unsafeWindow.tUronkills += unsafeWindow.Uronkills;
  1082. unsafeWindow.tUronkills2 += unsafeWindow.Uronkills2;
  1083. unsafeWindow.tPhysicalDamage += unsafeWindow.PhysicalDamage;
  1084. unsafeWindow.tPhysicalDamage2 += unsafeWindow.PhysicalDamage2;
  1085. if (![0, 1].includes(leap_distance)) cre_collection[attacker].attackaddon -= leap_atk_bonus;
  1086. let leap_display_distance = cre_distance ? "" : leap_distance;
  1087. return { distance: leap_display_distance, leap_atk_bonus: leap_atk_bonus }
  1088. }
  1089.  
  1090. function get_dmg_info(attacker_obj_index, defender_obj_index) {
  1091. let cre_collection = unsafeWindow.stage.pole.obj
  1092. let attacker = cre_collection[attacker_obj_index]
  1093. let defender = cre_collection[defender_obj_index]
  1094. let dmg_dict = attackmonster(attacker_obj_index, attacker.x, attacker.y, defender.x, defender.y, defender_obj_index, GM_getValue("cre_distance"));
  1095. let min_damage = unsafeWindow.PhysicalDamage
  1096. let max_damage = unsafeWindow.PhysicalDamage2
  1097. let min_killed, max_killed;
  1098. if (min_damage % defender.maxhealth > defender.nowhealth) min_killed = Math.floor(min_damage / defender.maxhealth) + 1
  1099. else min_killed = Math.floor(min_damage / defender.maxhealth)
  1100. if (max_damage % defender.maxhealth > defender.nowhealth) max_killed = Math.floor(max_damage / defender.maxhealth) + 1
  1101. else max_killed = Math.floor(max_damage / defender.maxhealth)
  1102. return { min: min_damage, max: max_damage, min_killed: min_killed, max_killed: max_killed, distance: dmg_dict.distance }
  1103. }
  1104.  
  1105. let defender_obj_id = 0
  1106. let selected_id = 0
  1107.  
  1108. function refresh() {
  1109. isOpen = true
  1110. let cre_collection = unsafeWindow.stage.pole.obj
  1111. if (cre_distance_on) {
  1112. cre_distance_div.style.display = "inline";
  1113. cre_distance_div.innerHTML = `<span>Выбранное расстояние: ${GM_getValue('cre_distance')}</span><br>`
  1114. }
  1115. set_Display([select, side_button, collapse_button, document.querySelector("#chosen_cre_heading"), dmg_list_container, individual_calc], "inline")
  1116.  
  1117. refresh_button.innerHTML = "Обновить"
  1118. let cre_list = Object.values(cre_collection);
  1119. cre_list.sort(function(a, b) {
  1120. return a.obj_index - b.obj_index;
  1121. });
  1122. dmg_list_container.innerHTML = "";
  1123. [...select.children].forEach(child => child.remove())
  1124. let found_defender = false
  1125. cre_list.forEach(defender => {
  1126. if (![0, -1].includes(defender.nownumber) && defender.nametxt != "" && defender.side == chosen.side && defender.hero == undefined) {
  1127. let option_id = `cre_no${cre_list.indexOf(defender)}`
  1128. select.insertAdjacentHTML("beforeend", `<option id = "${option_id}" value = "${defender.obj_index}">${defender.nametxt} [${defender.nownumber}] </option>`)
  1129. if (!found_defender) {
  1130. if (`${defender.obj_index}` == chosen.creature) found_defender = true
  1131. defender_obj_id = defender.obj_index
  1132. selected_id = [...select.children].indexOf(select.lastChild)
  1133. }
  1134. }
  1135. })
  1136. dmg_list_container.insertAdjacentHTML("beforeend", `<div id = "chosen_cre_heading" style="display:inline;">
  1137. <span>Урон по </span><span style="color:#ffffff; font-size: 110%; font-weight: bold;">${cre_list[defender_obj_id-1].nametxt} [${cre_list[defender_obj_id-1].nownumber}] :</span>
  1138. </div>`)
  1139. cre_list.forEach(attacker => {
  1140. if (attacker.side == -chosen.side && attacker.nownumber != 0 && attacker.nametxt != "") {
  1141. let dmg = get_dmg_info(attacker.obj_index, defender_obj_id)
  1142. let practical_overall_hp;
  1143. if (cre_list[defender_obj_id - 1].attack > attacker.defence) {
  1144. practical_overall_hp = attacker.maxhealth * attacker.nownumber / (1 + 0.05 * Math.abs(cre_list[defender_obj_id - 1].attack - attacker.defence))
  1145. } else {
  1146. practical_overall_hp = attacker.maxhealth * attacker.nownumber * (1 + 0.05 * Math.abs(cre_list[defender_obj_id - 1].attack - attacker.defence))
  1147. }
  1148. let row_id = `row_no${cre_list.indexOf(attacker)}`
  1149. let koef_string = `(коэф. урона <b>${( ((dmg.max + dmg.min) / 2) / practical_overall_hp ).toFixed(2)}</b>)`
  1150. dmg_list_container.insertAdjacentHTML("beforeend", `<p id = "${row_id}"><span style = "text-decoration: underline;color:#bfbfbf" >${attacker.nametxt}</span> [${attacker.nownumber}] --> <b style = "color:#bfbfbf">${dmg.min_killed}-${dmg.max_killed}</b> существ (${dmg.min}-${dmg.max}) ${(attacker.hero == undefined&&coeff_on) ? koef_string : ""} </p>`)
  1151. }
  1152. })
  1153. select.options.item(selected_id).selected = true
  1154. }