Kittens Game - Progress Bars

Adds progress bars to Kittens Game to see how close you are to an upgrade

目前为 2025-02-15 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Kittens Game - Progress Bars
  3. // @namespace https://greasyfork.org/en/scripts/526715-kittens-game-progress-bars
  4. // @version 1.2
  5. // @description Adds progress bars to Kittens Game to see how close you are to an upgrade
  6. // @author Mashiro-chan
  7. // @match https://kittensgame.com/web/
  8. // @license MIT
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. const init = () => {
  15. console.log("init ran!");
  16. if (!game.resPool) {
  17. setTimeout(init, 100);
  18. return;
  19. }
  20.  
  21. document.addEventListener("click", function(event) {
  22. const button = event.target.closest(".tab");
  23. if (button && !button.classList.contains("activeTab")) {
  24. updateButtons();
  25. }
  26. });
  27.  
  28. var getButtons = () => {
  29. var buttons = [game.tabs.find(t => t.tabId == game.ui.activeTabId)]
  30. .flatMap(t => [...new Set(Object.keys(t).filter(k => /btn|button|panel|children/i.test(k)).flatMap(k => t[k]))])
  31. .filter(Boolean);
  32. while (buttons.some(x => !x.model)) {
  33. buttons = buttons.flatMap(x =>
  34. x.model
  35. ? x
  36. : [x.tradeBtn, x.race, x.embassyButton, x.children].flat().filter(Boolean)
  37. );
  38. }
  39. return buttons;
  40. };
  41.  
  42. const extendPrices = prices => prices.map(p => ({
  43. 'name': p.name,
  44. 'have': game.resPool.get(p.name).value,
  45. 'need': p.val
  46. })).map(p => ({
  47. ...p,
  48. 'delta': p.need - p.have,
  49. 'percent': p.have / p.need
  50. }));
  51.  
  52. let progressBarColor = '#FF0000';
  53.  
  54. const bodyClass = document.body.className.match(/scheme_([\w-]+)/);
  55. const themeName = bodyClass[1];
  56. const themeStylesheet = `theme_${themeName}.css`;
  57. const selector = `.scheme_${themeName} .btn.modern.disabled.limited span.btnTitle`;
  58. const sheet = [...document.styleSheets].find(s => s.href && s.href.includes(themeStylesheet));
  59. const rule = [...sheet.cssRules].find(r => r.selectorText === selector);
  60.  
  61. progressBarColor = rule.style.color;
  62.  
  63. const initButtonExtension = button => {
  64. const buttonContent = button.buttonContent;
  65. if (!buttonContent) return;
  66.  
  67. const statusBar = document.createElement('div');
  68. statusBar.className = 'statusBar';
  69. Object.assign(statusBar.style, {
  70. display: 'none',
  71. position: 'absolute',
  72. bottom: '0px',
  73. left: '4%',
  74. height: '1px',
  75. width: '92%',
  76. pointerEvents: 'none'
  77. });
  78. buttonContent.appendChild(statusBar);
  79. button.statusBar = statusBar;
  80.  
  81. const progressBar = document.createElement('div');
  82. progressBar.className = 'progressBar';
  83. Object.assign(progressBar.style, {
  84. display: 'inline-block',
  85. float: 'left',
  86. height: '100%',
  87. width: '0%',
  88. backgroundColor: progressBarColor,
  89. borderRadius: '2px 2px 2px 2px'
  90. });
  91. statusBar.appendChild(progressBar);
  92. statusBar.progressBar = progressBar;
  93. };
  94.  
  95. const updateButtons = () => {
  96. document.querySelectorAll('.tabInner .btn.nosel .statusBar').forEach(el => {
  97. el.style.display = 'none';
  98. });
  99.  
  100. getButtons()
  101. .filter(b => b.model.visible && !b.model.enabled && b.buttonContent.offsetParent)
  102. .filter(b => !/\((?:complete|in progress)\)/i.test(b.model.name))
  103. .forEach(button => {
  104. if (!button.buttonContent.querySelector('.statusBar')) {
  105. initButtonExtension(button);
  106. }
  107.  
  108. const prices = extendPrices(button.model.prices);
  109. const minPercent = Math.max(Math.min(1, ...prices.map(p => p.percent)), 0.01);
  110. if (minPercent >= 1) return;
  111.  
  112. button.statusBar.style.display = 'inline-block';
  113. button.statusBar.progressBar.style.width = (minPercent * 100) + '%';
  114. });
  115. };
  116.  
  117. setInterval(() => {
  118. updateButtons();
  119. }, 100);
  120. };
  121.  
  122. if (document.readyState === 'loading') {
  123. document.addEventListener('DOMContentLoaded', init);
  124. } else {
  125. init();
  126. }
  127. })();