battle_damage_tooltip

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

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

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