LWM_ProgressBars

Insert level & skill progress bars to home page & player info page.

  1. // ==UserScript==
  2. // @name LWM_ProgressBars
  3. // @name:ru Прогресс персонажа
  4. // @description Insert level & skill progress bars to home page & player info page.
  5. // @description:ru Добавляет шкалу отображающую процент получения следующего уровня
  6. // @namespace saturn_hwm
  7. // @author saturn573
  8. // @homepage https://gf.qytechs.cn/scripts/2892-lwm-progressbars
  9. // @include https://178.248.235.15/home.php
  10. // @include https://*.lordswm.com/home.php
  11. // @include https://*.heroeswm.ru/home.php
  12. // @include https://178.248.235.15/pl_info.php?id=*
  13. // @include https://*.lordswm.com/pl_info.php?id=*
  14. // @include https://*.heroeswm.ru/pl_info.php?id=*
  15. // @version 0.33
  16. // @grant GM_setValue
  17. // @grant GM_getValue
  18. // @grant GM_deleteValue
  19. // ==/UserScript==
  20.  
  21. var Scales = [
  22. [0, 1500, 4500, 15E3, 32E3, 9E4, 19E4, 4E5, 86E4, 165E4, 3E6, 5E6, 85E5, 145E5, 25E6, 43E6, 7E7, 108E6, 16E7, 23E7, 325E6, 5E8, 8E8],
  23. [20, 50, 90, 160, 280, 500, 900, 1600, 2900, 5300, 9600, 17300, 35E3, 7E4],
  24. [16, 60, 180, 400, 700, 1200, 2E3, 3E3, 4300, 6E3, 8E3, 10500, 13100],
  25. [90, 180, 360, 720, 1500, 3E3, 5E3, 8E3, 12E3, 17E3, 23E3, 3E4, 38E3, 47E3, 57E3, 7E4, 9E4],
  26. [10, 30, 60, 100, 150, 210, 280, 360, 450, 550, 660, 800, 1E3, 1300, 2E3, 3E3, 6E3, 1E4, 17E3, 25E3],
  27. [50, 120, 240, 400, 600, 840, 1200, 2E3, 3E3, 4300, 6E3, 8E3, 10800, 14E3, 17600, 21600, 26E3, 30800, 36600, 43600, 52E3, 65E3],
  28. [100, 240, 480, 800, 1200, 1680, 2400, 4E3, 6E3, 8600, 12E3, 16E3, 21600],
  29. [50, 120, 300, 600, 1E3, 1500, 2200, 3E3, 4E3, 5500, 7800, 11E3, 14500, 18200, 22200],
  30. [150, 350, 750, 1400, 2200, 3200, 4300, 5600, 7E3, 8500, 1E4, 11700, 14500],
  31. [60, 200, 450, 850, 1500, 2700, 4500, 7200],
  32. [1600, 3600, 8100],
  33. [80, 180, 300, 440, 600, 780, 990, 1230, 1500, 2200, 3200, 4500, 7E3, 11E3],
  34. [30, 80, 165, 310, 555, 970, 1680, 2885, 5770],
  35. [104, 588, 2200, 7E3, 1E4],
  36. [8, 29, 71, 155, 295, 505, 799, 1191, 1695, 6E3, 12E3]];
  37.  
  38. var Styles = '.pb{ display:inline-block; position: relative; width:135px; background:white; border:2px solid; border-radius: 7px/3px; cursor: pointer; }\
  39. .pb .scale { display: inline; position: absolute; top: 0px; left: 0px; height: 100%; border-radius: 2px/1px; background:#af9f39; background: linear-gradient(to top, #af9f39, #fffbca); }\
  40. .pb:hover .pb-side-text { display: inline; }\
  41. .pb:hover .pb-front-text { display: none; }\
  42. .pb-text { position: relative; width: 100%; height: 100%; color: darkgreen; text-align: center; font-weight: bold; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }\
  43. .pb-side-text { display:none; }\
  44. .pb-front-text { display: inline; }\
  45. .left { font-size: smaller; }\
  46. .levelpb { display: inline-flex; margin-left: 10px; width: 180px; } \
  47. .skill-table { width: 100%; margin-bottom: 10px; }\
  48. .skill-table caption { text-align: left; }\
  49. .skill-table caption span { float: right; font-size: small; margin-right: 10; }\
  50. .key-column { text-align: left; width: 100%; font-variant: small-caps; }\
  51. .skill-value { font-size: 15px; }\
  52. .skill-value-complete { color: #af9f39; font-size: 20px; font-weight: bold; font-family: "Comic Sans MS", cursive, sans-serif; }\
  53. .skill-value-none { font-weight: normal; }\
  54. .skill-value-low { color: blue; }\
  55. .progress-column { text-align: center; width: 135px; }\
  56. .expander { margin-left: 5px; border-radius: 8px; height: 16px; width: 16px; font-size: 8px; font-weight:bold; background: gold; background: linear-gradient(to top, #af9f39, #fffbca); border: #af9f39; border-style: outset; border-width: 1px; position: relative; top: -3px; }';
  57.  
  58. var expandCaption = '>';
  59. var collapseCaption = '<';
  60. var levelStringRU = '\u0411\u043E\u0435\u0432\u043E\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C';
  61. var factionsGroupTitleRU = '\u0424\u0440\u0430\u043A\u0446\u0438\u044F';
  62. var guildsGroupTitleRU = '\u0413\u0438\u043B\u044C\u0434\u0438\u044F';
  63. var allRu = '\u043E\u0431\u0449.';
  64. var hoursRu = 'часов';
  65. var daysRu = 'дней';
  66. var weeksRu = 'недель';
  67. var pointsRu = 'очков за';
  68. var pphRu = 'очков в час';
  69. var averageSpeedRu = 'Средняя скорость';
  70. var tttnlRu = 'Время до следующего уровня';
  71.  
  72.  
  73. var pl_id = /pl_id=(\d+)/.exec(document.cookie)[1];
  74. if (~location.pathname.indexOf('pl_info.php')) {
  75. pl_id = /id=(\d+)/.exec(location.search)[1];
  76. }
  77.  
  78. function getTrackingState(){
  79. let result = GM_getValue('pl_' + pl_id);
  80. if (result) {
  81. result = JSON.parse(result);
  82. }
  83. else {
  84. result = undefined;
  85. }
  86. return result;
  87. }
  88.  
  89. function startTracking() {
  90. GM_setValue('pl_' + pl_id, JSON.stringify(currentState));
  91. alert((isEn() ? 'The tracking was started. Click again to cancel it.'
  92. : 'Отслеживание начато. Нажмите еще раз чтобы выключить его'));
  93. location.reload();
  94. }
  95.  
  96. function stopTracking() {
  97. if (confirm((isEn()
  98. ? 'The tracking will be stopped immediately as you press the OK button.'
  99. : 'Отслеживание будет выключено сразу после того как вы нажмете кнопку ОК'))) {
  100. GM_deleteValue("pl_" + pl_id);
  101. location.reload();
  102. }
  103. }
  104.  
  105. var currentState = { time: new Date().getTime() };
  106. var trackingState = getTrackingState();
  107.  
  108. function getTimeLabel(hours) {
  109. if (hours < 24) {
  110. return hours + ' ' + (isEn() ? 'hours' : hoursRu);
  111. }
  112. else {
  113. var days = parseInt(hours / 24);
  114. if (days < 7) {
  115. return days + ' ' + (isEn() ? 'days' : daysRu)
  116. + ' ' + (hours % 24) + ' ' + (isEn() ? 'hours' : hoursRu);
  117. }
  118. else {
  119. return '~' + parseInt(days / 7) + ' ' + (isEn() ? 'weeks' : weeksRu);
  120. }
  121. }
  122. }
  123.  
  124. function getTrackingInfoString(caption, left) {
  125. let delta = currentState[caption];
  126. if (trackingState[caption]) {
  127. delta -= trackingState[caption];
  128. }
  129. let elapsedHours = Math.floor((currentState.time - trackingState.time) / 36E5);
  130. let speed = delta / elapsedHours;
  131. return delta.toFixed(2)
  132. + ' ' + (isEn() ? 'points in' : pointsRu) + ' '
  133. + getTimeLabel(elapsedHours)
  134. + ((elapsedHours > 0 && delta > 0)
  135. ? ' \r\n'
  136. + (isEn() ? 'Average speed' : averageSpeedRu)
  137. + ': ' + speed.toFixed(2) + ' '
  138. + (isEn() ? 'points per hour' : pphRu)
  139. + '\r\n'
  140. + (isEn() ? 'Time to the next level' : tttnlRu)
  141. + ': ' + getTimeLabel(Math.floor(left / speed))
  142. : '');
  143. }
  144.  
  145.  
  146. function round(value, precision) {
  147. if (precision > 0) {
  148. var b = precision * 10;
  149. return Math.round(value * b) / b;
  150. }
  151. else {
  152. return Math.floor(value);
  153. }
  154. }
  155.  
  156. function parseSourceCode(source) {
  157. var captions = [];
  158. var match;
  159. var cr = /((?:[a-z'\u0430-\u044F\u0451]+\s)?[a-z\u0430-\u044F\u0451]+):\s/gi;
  160. while ((match = cr.exec(source)) != null) {
  161. captions.push({ Index: cr.lastIndex, Value: match[0].toString() });
  162. }
  163. var getCaption = function (index) {
  164. for (var ii = captions.length - 1; ii >= 0; ii--) {
  165. if (captions[ii].Index < index) {
  166. return captions[ii].Value;
  167. }
  168. }
  169. return null;
  170. }
  171.  
  172. var result = [];
  173. var sr = /(?:\s|>)(\d+)(<\/b>|<\/a>)?\s\((\d+(?:\.\d+)?)\)/g;
  174. while ((match = sr.exec(source)) != null) {
  175. let item = {
  176. Caption: getCaption(sr.lastIndex),
  177. Level: +match[1].toString(),
  178. Score: +match[3].toString(),
  179. Sign: match[2]
  180. };
  181. currentState[item.Caption] = item.Score;
  182. result.push(item);
  183. }
  184. return result;
  185. }
  186.  
  187. function addStyles() {
  188. var style = document.createElement('style');
  189. style.type = 'text/css';
  190. style.appendChild(document.createTextNode(Styles));
  191. document.head.appendChild(style);
  192. }
  193.  
  194. function checkScale(scale) {
  195. if (scale && scale.length > 0) {
  196. if (scale.length > 1) {
  197. for (var ii = 1; ii < scale.length; ii++) {
  198. if (scale[ii] <= scale[ii - 1]) {
  199. return false;
  200. }
  201. }
  202. }
  203. return true;
  204. }
  205. }
  206.  
  207. function createProgressBar(points, left, percentage, title) {
  208. var percentageString = Math.floor(percentage) + '%';
  209.  
  210. var border = document.createElement('div');
  211. border.className = 'pb';
  212.  
  213. var scale = document.createElement('div');
  214. scale.className = 'scale';
  215. scale.style.width = percentageString;
  216. border.appendChild(scale);
  217.  
  218. var textBox = document.createElement('div');
  219. textBox.className = 'pb-text';
  220.  
  221. var frontText = document.createElement('span');
  222. frontText.className = 'pb-front-text';
  223. frontText.appendChild(document.createTextNode(percentageString));
  224. textBox.appendChild(frontText);
  225.  
  226. var sideText = document.createElement('span');
  227. sideText.className = 'pb-side-text';
  228. sideText.appendChild(document.createTextNode(points));
  229.  
  230. var l = document.createElement('span');
  231. l.className = 'left';
  232. l.appendChild(document.createTextNode('+' + left));
  233. sideText.appendChild(l);
  234. textBox.appendChild(sideText);
  235.  
  236. border.appendChild(textBox);
  237.  
  238. if (title) {
  239. if (trackingState) {
  240. border.title = getTrackingInfoString(title, left);
  241. border.addEventListener('click', stopTracking);
  242. }
  243. else {
  244. border.title = isEn()
  245. ? 'Click to start tracking'
  246. : 'Нажмите чтобы начать отслеживание';
  247. border.addEventListener('click', startTracking);
  248. }
  249. }
  250. return border;
  251. }
  252.  
  253. function getScoreRange(score, scale) {
  254. if (!checkScale(scale)) {
  255. return;
  256. }
  257.  
  258. var initialValue = 0;
  259. var finalValue = score;
  260. var level = 0;
  261. for (var ii = 0; ii < scale.length; ii++) {
  262. if (score >= scale[ii]) {
  263. initialValue = scale[ii];
  264. }
  265. else {
  266. finalValue = scale[ii];
  267. level = ii;
  268. break;
  269. }
  270. }
  271. return { Initial: initialValue, Final: finalValue, Level: level };
  272. }
  273.  
  274. function getProgressPercentage(range, score) {
  275. if (range) {
  276. return (score - range.Initial) * 100 / (range.Final - range.Initial);
  277. }
  278. return 0;
  279. }
  280.  
  281. function getScale(index) {
  282. return Scales[index];
  283. }
  284.  
  285. function getCaption(value, exclude) {
  286. if (exclude) {
  287. var r = new RegExp(exclude, 'i');
  288. return value.Caption.replace(r, '');
  289. }
  290. return value.Caption;
  291. }
  292.  
  293. function createRow(value, scaleIndex, excludeCaption) {
  294. var scale = getScale(scaleIndex);
  295. var range = getScoreRange(value.Score, scale);
  296. if (!range) {
  297. alert(JSON.stringify(value) + ' : ' + scaleIndex + ' - ' + excludeCaption);
  298. }
  299.  
  300. var row = document.createElement('tr');
  301. var c1 = document.createElement('td');
  302. c1.className = 'key-column';
  303. if (scaleIndex == 1 && value.Sign) {
  304. c1.style = 'font-weight: bold; text-decoration:underline; ';
  305. }
  306. c1.appendChild(document.createTextNode(getCaption(value, excludeCaption)));
  307. var lb = document.createElement('b');
  308. lb.className = 'skill-value';
  309. lb.appendChild(document.createTextNode(value.Level));
  310. if (value.Score >= range.Final) {
  311. lb.className = 'skill-value-complete';
  312. }
  313. else if (!value.Score) {
  314. lb.className = 'skill-value-none';
  315. }
  316. c1.appendChild(lb);
  317. row.appendChild(c1);
  318. var c2 = document.createElement('td');
  319. c2.className = 'progress-column';
  320. if (value.Score == 0) {
  321. c2.appendChild(document.createTextNode('-'));
  322. }
  323. else if (value.Score >= range.Final) {
  324. c2.appendChild(document.createTextNode(value.Score));
  325. }
  326. else {
  327. var percentage = getProgressPercentage(range, value.Score);
  328.  
  329. var points = value.Score;
  330. var left = round(range.Final - value.Score, 1);
  331.  
  332. if (range.Level > value.Level) {
  333. percentage = 100;
  334. left = '0.1';
  335. var rl = document.createElement('small');
  336. rl.innerHTML = -(range.Level - value.Level);
  337. lb.className = 'skill-value-low';
  338. c1.appendChild(rl);
  339. }
  340. var pb = createProgressBar(points, left, percentage, value.Caption);
  341. c2.appendChild(pb);
  342. }
  343. row.appendChild(c2);
  344. return row;
  345. }
  346.  
  347. function createExpanderButton() {
  348. var cb = document.createElement('input');
  349. cb.type = 'button';
  350. cb.value = expandCaption;
  351. cb.className = 'expander';
  352. cb.collapsed = true;
  353. cb.onclick = function (event) {
  354. var r = this.parentNode.parentNode;
  355. var display = this.collapsed ? '' : 'none';
  356. r.nextSibling.style.display = display;
  357. r.nextSibling.nextSibling.style.display = display;
  358. r.nextSibling.nextSibling.nextSibling.style.display = display;
  359. this.collapsed = !this.collapsed;
  360. this.value = this.collapsed ? expandCaption : collapseCaption;
  361. };
  362. return cb;
  363. }
  364.  
  365. function insertExpander(table) {
  366. var row = table.lastChild;
  367. for (var ii = 0; ii < 3; ii++) {
  368. row.firstChild.style = 'padding-left: 15px;';
  369. row.style.display = 'none';
  370. row = row.previousSibling;
  371. }
  372. row.firstChild.appendChild(createExpanderButton());
  373. }
  374.  
  375. function isEn() {
  376. return /^www\.lordswm\.com/.test(location.host);
  377. }
  378.  
  379. function replaceSkills() {
  380. var home = document.getElementById('home_2');
  381. if (home) {
  382. var createSkillTable = function(caption) {
  383. var result = document.createElement('table');
  384. result.className = 'skill-table';
  385. var cpt = document.createElement('caption');
  386. cpt.appendChild(document.createTextNode(caption));
  387. result.appendChild(cpt);
  388. return result;
  389. }
  390.  
  391. var mainNode = home.parentNode;
  392. var items = parseSourceCode(mainNode.innerHTML.toString());
  393.  
  394. var range = document.createRange();
  395. range.selectNodeContents(mainNode);
  396. range.deleteContents();
  397.  
  398. var t = createSkillTable(isEn() ? 'Factions' : factionsGroupTitleRU);
  399. var excludeCaption;
  400. var ii = 0;
  401. var gi = 0;
  402. var faction = true;
  403. var factionPoints = 0;
  404. do {
  405. if (faction && (~items[ii].Caption.indexOf(isEn() ? 'guild' : '\u0413\u0438\u043B\u044C\u0434\u0438\u044F'))) {
  406. faction = false;
  407. gi = ii;
  408. var sum = document.createElement('span');
  409. sum.appendChild(document.createTextNode(' \u03A3'));
  410. var sub = document.createElement('sub');
  411. sub.appendChild(document.createTextNode(isEn() ? 'all' : allRu));
  412. sum.appendChild(sub);
  413. sum.appendChild(document.createTextNode('= ' + round(factionPoints, 2)));
  414. t.firstChild.appendChild(sum);
  415. mainNode.appendChild(t);
  416. t = createSkillTable(isEn() ? 'Guilds' : guildsGroupTitleRU);
  417. excludeCaption = "('\\sguild)|(" + guildsGroupTitleRU + ')';
  418. mainNode.appendChild(t);
  419. }
  420. if (faction) {
  421. factionPoints += items[ii].Score;
  422. }
  423. t.appendChild(createRow(items[ii], (faction ? 1 : Math.min(Scales.length - 1, (ii - gi) + 2)), excludeCaption));
  424. ii++;
  425. }
  426. while (ii < items.length - 2);
  427. insertExpander(t);
  428. mainNode.appendChild(t);
  429. }
  430. }
  431.  
  432. function getLevelPoints(points) {
  433. var r = /\(([\d\,]+)\)(?:\s\+(-?[\d\,]+))?/;
  434. var m = r.exec(points)
  435. if (m) {
  436. return { Points: m[1].replace(/,/g, ''), Left: (m[2] ? m[2].replace(/,/g, '') : 0) }
  437. }
  438. }
  439.  
  440. function insertLevelUpProgressBar() {
  441. var bs = document.getElementsByTagName('b');
  442. var lbReg = new RegExp('(?:Combat\\slevel|' + levelStringRU + '):\\s(\\d+)');
  443. for (var ii = 0; ii < bs.length; ii++) {
  444. var b = bs[ii];
  445. var m = lbReg.exec(b.innerHTML);
  446. if (m) {
  447. var start = b.nextSibling;
  448. var end = start;
  449. do {
  450. end = end.nextSibling;
  451. }
  452. while (end && (!end.tagName || end.tagName.toLowerCase() != 'br'));
  453. var next = end.nextSibling;
  454. if (end) {
  455. var pointsText;
  456. var range = document.createRange();
  457. range.setStart(start, 0);
  458. range.setEnd(end, 0);
  459. pointsText = range.toString();
  460. var lvl = getLevelPoints(range.toString());
  461. if (lvl) {
  462. var r = getScoreRange(lvl.Points, getScale(0));
  463. if (r && r.Final > lvl.Points) {
  464. var percentage = getProgressPercentage(r, lvl.Points);
  465. if (r.Level > m[1]) {
  466. b.appendChild(document.createTextNode('+'));
  467. percentage = 100;
  468. }
  469. currentState.level = lvl.Points;
  470. var pb = createProgressBar(lvl.Points, lvl.Left, percentage, 'level');
  471. pb.className += ' levelpb';
  472. b.parentNode.insertBefore(pb, b.nextSibling);
  473. range.deleteContents();
  474. }
  475. }
  476. range.detach();
  477. break;
  478. }
  479. }
  480. }
  481. }
  482.  
  483. addStyles(); insertLevelUpProgressBar(); replaceSkills();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址