Survev UI Mod v5

QoL features for Survev.io (works with expandedwater, untested)

  1. // ==UserScript==
  2. // @name Survev UI Mod v5
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-08-31
  5. // @description QoL features for Survev.io (works with expandedwater, untested)
  6. // @author Blubbled
  7. // @match https://survev.io/*
  8. // @match https://expandedwater.online/
  9. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  10. // @grant none
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14.  
  15.  
  16. (function() {
  17.  
  18. var lastCalledTime;
  19. var fps;
  20. var frameTimes = [];
  21. var maxFrames = 100;
  22. var uncappedFPS = false;
  23. var uiElementsEnabled = true;
  24. var fpsCounterEnabled = true;
  25. var currentCountdown = null;
  26. var healthBarSettingsCreated = false;
  27. var switchDelayEnabled = localStorage.getItem('switchDelayEnabled') === 'true';
  28. var customColor = localStorage.getItem('customColor') || '#ffffff';
  29. var colorToggleState = localStorage.getItem('colorToggle') === 'true';
  30.  
  31. function requestAnimFrame() {
  32. if (!lastCalledTime) {
  33. lastCalledTime = Date.now();
  34. fps = 0;
  35. return;
  36. }
  37.  
  38. var currentTime = Date.now();
  39. var delta = (currentTime - lastCalledTime) / 1000;
  40. lastCalledTime = currentTime;
  41.  
  42. frameTimes.push(delta);
  43.  
  44. if (frameTimes.length > maxFrames) {
  45. frameTimes.shift();
  46. }
  47.  
  48. var totalTime = frameTimes.reduce((sum, time) => sum + time, 0);
  49. fps = (frameTimes.length / totalTime).toFixed(0);
  50. }
  51.  
  52. function createFPSCounter() {
  53. var fpsCounter = document.createElement('div');
  54. fpsCounter.id = 'fps-counter';
  55. fpsCounter.style.position = 'fixed';
  56. fpsCounter.style.left = '10px';
  57. fpsCounter.style.top = '130px';
  58. fpsCounter.style.color = customColor;
  59. fpsCounter.style.fontSize = '20px';
  60. fpsCounter.style.fontWeight = 'bold';
  61. fpsCounter.style.zIndex = '1000';
  62. fpsCounter.style.backgroundColor = 'rgba(0, 0, 0, 0)';
  63. fpsCounter.style.padding = '5px';
  64. fpsCounter.style.borderRadius = '5px';
  65. document.body.appendChild(fpsCounter);
  66.  
  67. var lastUpdate = Date.now();
  68.  
  69. function updateFPSCounter() {
  70. requestAnimFrame();
  71. var now = Date.now();
  72. if (now - lastUpdate >= 1000) {
  73. fpsCounter.textContent = `FPS: ${fps}`;
  74. lastUpdate = now;
  75. }
  76.  
  77. requestAnimationFrame(updateFPSCounter);
  78. }
  79.  
  80. updateFPSCounter();
  81. }
  82.  
  83. createFPSCounter();
  84.  
  85. function toggleFPSCounter(enabled) {
  86. var fpsCounter = document.getElementById('fps-counter');
  87. if (enabled) {
  88. fpsCounter.style.display = 'block';
  89. } else {
  90. fpsCounter.style.display = 'none';
  91. }
  92. }
  93.  
  94. function resetTextColors() {
  95. var fpsCounter = document.getElementById('fps-counter');
  96. var ammoCount = document.getElementById('ui-current-clip');
  97. var healCounts = document.querySelectorAll('.ui-loot-count');
  98.  
  99. if (fpsCounter) fpsCounter.style.color = '';
  100. if (ammoCount) ammoCount.style.color = '';
  101. healCounts.forEach(function(healCount) {
  102. healCount.style.color = '';
  103. });
  104. }
  105.  
  106. function updateTextColor(newColor) {
  107. if (!colorToggleState) {
  108. resetTextColors();
  109. return;
  110. }
  111.  
  112. customColor = newColor;
  113. localStorage.setItem('customColor', newColor);
  114.  
  115. var fpsCounter = document.getElementById('fps-counter');
  116. var ammoCount = document.getElementById('ui-current-clip');
  117. var healCounts = document.querySelectorAll('.ui-loot-count');
  118.  
  119. if (fpsCounter) fpsCounter.style.color = newColor;
  120. if (ammoCount) ammoCount.style.color = newColor;
  121. healCounts.forEach(function(healCount) {
  122. healCount.style.color = newColor;
  123. });
  124. }
  125.  
  126. function applyInitialTextColor() {
  127. if (!colorToggleState) {
  128. return;
  129. }
  130.  
  131. var ammoCount = document.getElementById('ui-current-clip');
  132. var healCounts = document.querySelectorAll('.ui-loot-count');
  133.  
  134. if (ammoCount) ammoCount.style.color = customColor;
  135. healCounts.forEach(function(healCount) {
  136. healCount.style.color = customColor;
  137. });
  138. }
  139.  
  140. function applyColorOnGameUI() {
  141. var observer = new MutationObserver(function(mutations) {
  142. mutations.forEach(function(mutation) {
  143. if (mutation.addedNodes.length) {
  144. applyInitialTextColor();
  145. }
  146. });
  147. });
  148.  
  149. observer.observe(document.body, { childList: true, subtree: true });
  150. }
  151.  
  152. var uiTopLeft = document.getElementById('ui-top-left');
  153. if (uiTopLeft) {
  154. uiTopLeft.style.position = 'relative';
  155. uiTopLeft.style.left = '100px';
  156. }
  157.  
  158. document.addEventListener("DOMContentLoaded", function() {
  159. if (colorToggleState) {
  160. applyInitialTextColor();
  161. }
  162.  
  163. applyColorOnGameUI();
  164. });
  165.  
  166. function createSwitchDelayToggle(settingsTab) {
  167. var switchDelayLabel = document.createElement('label');
  168. switchDelayLabel.textContent = 'Show Switch Delay';
  169. settingsTab.appendChild(switchDelayLabel);
  170.  
  171. var switchDelayCheckbox = document.createElement('input');
  172. switchDelayCheckbox.type = 'checkbox';
  173. switchDelayCheckbox.id = 'switch-delay-checkbox';
  174. switchDelayCheckbox.checked = switchDelayEnabled;
  175. settingsTab.appendChild(switchDelayCheckbox);
  176. settingsTab.appendChild(document.createElement('br'));
  177.  
  178. switchDelayCheckbox.addEventListener('change', function() {
  179. switchDelayEnabled = switchDelayCheckbox.checked;
  180. localStorage.setItem('switchDelayEnabled', switchDelayEnabled);
  181. });
  182. }
  183.  
  184. var settingsButton = document.createElement('button');
  185. settingsButton.id = 'settings-button';
  186. settingsButton.textContent = 'Mod Settings';
  187. settingsButton.style.position = 'fixed';
  188. settingsButton.style.left = '10px';
  189. settingsButton.style.top = '50%';
  190. settingsButton.style.transform = 'translateY(-50%)';
  191. settingsButton.style.padding = '10px';
  192. settingsButton.style.fontSize = '18px';
  193. settingsButton.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
  194. settingsButton.style.color = 'white';
  195. settingsButton.style.zIndex = '1000';
  196. settingsButton.style.display = 'none';
  197. document.body.appendChild(settingsButton);
  198.  
  199. var settingsTab = document.createElement('div');
  200. settingsTab.id = 'settings-tab';
  201. settingsTab.style.position = 'fixed';
  202. settingsTab.style.left = '200px';
  203. settingsTab.style.top = '50%';
  204. settingsTab.style.width = '300px';
  205. settingsTab.style.height = '400px';
  206. settingsTab.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
  207. settingsTab.style.color = 'white';
  208. settingsTab.style.padding = '20px';
  209. settingsTab.style.borderRadius = '10px';
  210. settingsTab.style.display = 'none';
  211. settingsTab.innerHTML = '<h2>Mod Settings (buggy, reenable uncapped FPS a few times if cursor info doesnt update)</h2>';
  212. document.body.appendChild(settingsTab);
  213.  
  214. var fpsSettingWrapper = document.createElement('div');
  215. fpsSettingWrapper.style.textAlign = 'left';
  216.  
  217. var fpsLabel = document.createElement('label');
  218. fpsLabel.textContent = 'Enable Uncapped FPS';
  219. fpsLabel.setAttribute('for', 'uncapped-fps-checkbox');
  220.  
  221. var fpsCheckbox = document.createElement('input');
  222. fpsCheckbox.type = 'checkbox';
  223. fpsCheckbox.id = 'uncapped-fps-checkbox';
  224.  
  225. fpsSettingWrapper.appendChild(fpsLabel);
  226. fpsSettingWrapper.appendChild(fpsCheckbox);
  227.  
  228. settingsTab.appendChild(fpsSettingWrapper);
  229.  
  230. var uiSettingWrapper = document.createElement('div');
  231. uiSettingWrapper.style.textAlign = 'left';
  232.  
  233. var uiLabel = document.createElement('label');
  234. uiLabel.textContent = 'Show Cursor Info';
  235. uiLabel.setAttribute('for', 'ui-elements-checkbox');
  236.  
  237. var uiCheckbox = document.createElement('input');
  238. uiCheckbox.type = 'checkbox';
  239. uiCheckbox.id = 'ui-elements-checkbox';
  240.  
  241. uiSettingWrapper.appendChild(uiLabel);
  242. uiSettingWrapper.appendChild(uiCheckbox);
  243.  
  244. settingsTab.appendChild(uiSettingWrapper);
  245.  
  246. var fpsCounterWrapper = document.createElement('div');
  247. fpsCounterWrapper.style.textAlign = 'left';
  248.  
  249. var fpsCounterLabel = document.createElement('label');
  250. fpsCounterLabel.textContent = 'Show FPS Counter';
  251. fpsCounterLabel.setAttribute('for', 'fps-counter-checkbox');
  252.  
  253. var fpsCounterCheckbox = document.createElement('input');
  254. fpsCounterCheckbox.type = 'checkbox';
  255. fpsCounterCheckbox.id = 'fps-counter-checkbox';
  256.  
  257. fpsCounterWrapper.appendChild(fpsCounterLabel);
  258. fpsCounterWrapper.appendChild(fpsCounterCheckbox);
  259.  
  260. settingsTab.appendChild(fpsCounterWrapper);
  261.  
  262. createSwitchDelayToggle(settingsTab);
  263. settingsTab.appendChild(document.createElement('br'));
  264.  
  265. function toggleUncappedFPS(enabled) {
  266. if (enabled) {
  267. window.requestAnimationFrame = function(callback) {
  268. return setTimeout(callback, 1);
  269. };
  270. } else {
  271. window.requestAnimationFrame = function(callback) {
  272. return setTimeout(callback, 1000 / 60);
  273. };
  274. }
  275. }
  276.  
  277.  
  278.  
  279. var colorToggleLabel = document.createElement('label');
  280. colorToggleLabel.textContent = 'Enable Custom Text Color';
  281. settingsTab.appendChild(colorToggleLabel);
  282.  
  283. var colorToggleCheckbox = document.createElement('input');
  284. colorToggleCheckbox.type = 'checkbox';
  285. colorToggleCheckbox.id = 'color-toggle-checkbox';
  286. colorToggleCheckbox.checked = colorToggleState;
  287. settingsTab.appendChild(colorToggleCheckbox);
  288. settingsTab.appendChild(document.createElement('br'));
  289.  
  290. var colorPickerLabel = document.createElement('label');
  291. colorPickerLabel.textContent = 'Select Text Color';
  292. settingsTab.appendChild(colorPickerLabel);
  293.  
  294. var colorPicker = document.createElement('input');
  295. colorPicker.type = 'color';
  296. colorPicker.value = customColor;
  297. colorPicker.disabled = !colorToggleState;
  298. settingsTab.appendChild(colorPicker);
  299. settingsTab.appendChild(document.createElement('br'));
  300.  
  301. colorToggleCheckbox.addEventListener('change', function() {
  302. colorToggleState = colorToggleCheckbox.checked;
  303. colorPicker.disabled = !colorToggleState;
  304. if (colorToggleState) {
  305. updateTextColor(colorPicker.value);
  306. } else {
  307. resetTextColors();
  308. }
  309. localStorage.setItem('colorToggle', colorToggleState);
  310. });
  311. colorPicker.addEventListener('input', function() {
  312. if (colorToggleState) {
  313. updateTextColor(colorPicker.value);
  314. }
  315. });
  316.  
  317. function updateSettingsButtonVisibility() {
  318. var loadingOverlay = document.querySelector('.play-loading-outer');
  319.  
  320. if (loadingOverlay && window.getComputedStyle(loadingOverlay).opacity === '0') {
  321. settingsButton.style.display = 'block';
  322. } else {
  323. settingsButton.style.display = 'none';
  324. settingsTab.style.display = 'none';
  325. }
  326.  
  327. if (!healthBarSettingsCreated) {
  328. createHealthBarSettingsUI(settingsTab);
  329. healthBarSettingsCreated = true;
  330. }
  331.  
  332. if (healthBarColorToggleState) {
  333. observeHealthBar();
  334. }
  335. }
  336.  
  337. setInterval(updateSettingsButtonVisibility, 100);
  338.  
  339. settingsButton.addEventListener('click', function() {
  340. if (settingsTab.style.display === 'none') {
  341. settingsTab.style.display = 'block';
  342. } else {
  343. settingsTab.style.display = 'none';
  344. }
  345. });
  346.  
  347. function loadSettings() {
  348. var uncappedFPSSetting = localStorage.getItem('uncappedFPS') === 'true';
  349. var uiElementsSetting = localStorage.getItem('uiElementsEnabled') === 'true';
  350. var fpsCounterSetting = localStorage.getItem('fpsCounterEnabled') === 'true';
  351.  
  352. fpsCheckbox.checked = uncappedFPSSetting;
  353. uiCheckbox.checked = uiElementsSetting;
  354. fpsCounterCheckbox.checked = fpsCounterSetting;
  355.  
  356. toggleUncappedFPS(uncappedFPSSetting);
  357. toggleUIElementDisplay(uiElementsSetting);
  358. toggleFPSCounter(fpsCounterSetting);
  359.  
  360. if (colorToggleState) {
  361. updateTextColor(customColor);
  362. }
  363. }
  364.  
  365. function saveSettings() {
  366. var uncappedFPSSetting = fpsCheckbox.checked;
  367. var uiElementsSetting = uiCheckbox.checked;
  368. var fpsCounterSetting = fpsCounterCheckbox.checked;
  369.  
  370. localStorage.setItem('uncappedFPS', uncappedFPSSetting);
  371. localStorage.setItem('uiElementsEnabled', uiElementsSetting);
  372. localStorage.setItem('fpsCounterEnabled', fpsCounterSetting);
  373.  
  374. toggleUncappedFPS(uncappedFPSSetting);
  375. toggleUIElementDisplay(uiElementsSetting);
  376. toggleFPSCounter(fpsCounterSetting);
  377. }
  378.  
  379. loadSettings();
  380.  
  381. fpsCheckbox.addEventListener('change', saveSettings);
  382. uiCheckbox.addEventListener('change', saveSettings);
  383. fpsCounterCheckbox.addEventListener('change', saveSettings);
  384.  
  385. var healthBarColor = localStorage.getItem('healthBarColor') || '#00ff00';
  386. var healthBarDegradationColor = localStorage.getItem('healthBarDegradationColor') || '#ff0000';
  387. var healthBarColorToggleState = localStorage.getItem('healthBarColorToggle') === 'true';
  388.  
  389. function interpolateColor(color1, color2, factor) {
  390. var result = color1.slice(1).match(/.{2}/g).map((hex, i) => {
  391. return Math.round(parseInt(hex, 16) * (1 - factor) + parseInt(color2.slice(1).match(/.{2}/g)[i], 16) * factor).toString(16).padStart(2, '0');
  392. });
  393. return `#${result.join('')}`;
  394. }
  395.  
  396.  
  397. function updateHealthBarColor(percentage) {
  398. if (!healthBarColorToggleState) return;
  399. var interpolatedColor = interpolateColor(healthBarColor, healthBarDegradationColor, 1 - percentage);
  400. var healthBar = document.getElementById('ui-health-actual');
  401. if (healthBar) {
  402. healthBar.style.backgroundColor = interpolatedColor;
  403. }
  404. }
  405.  
  406. function observeHealthBar() {
  407. var healthBar = document.getElementById('ui-health-actual');
  408. if (!healthBar) return;
  409.  
  410. var observer = new MutationObserver(function() {
  411. var width = parseFloat(healthBar.style.width);
  412. var percentage = width / 100;
  413. updateHealthBarColor(percentage);
  414. });
  415.  
  416. observer.observe(healthBar, { attributes: true, attributeFilter: ['style'] });
  417. }
  418.  
  419.  
  420. function createHealthBarSettingsUI(settingsTab) {
  421.  
  422. var healthBarColorToggleLabel = document.createElement('label');
  423. settingsTab.appendChild(document.createElement('br'));
  424. healthBarColorToggleLabel.textContent = 'Enable Custom Health Bar Color';
  425. settingsTab.appendChild(healthBarColorToggleLabel);
  426.  
  427. var healthBarColorToggleCheckbox = document.createElement('input');
  428. healthBarColorToggleCheckbox.type = 'checkbox';
  429. healthBarColorToggleCheckbox.id = 'health-bar-color-toggle-checkbox';
  430. healthBarColorToggleCheckbox.checked = healthBarColorToggleState;
  431. settingsTab.appendChild(healthBarColorToggleCheckbox);
  432. settingsTab.appendChild(document.createElement('br'));
  433.  
  434.  
  435. var healthBarColorLabel = document.createElement('label');
  436. healthBarColorLabel.textContent = 'Health Bar Color:';
  437. settingsTab.appendChild(healthBarColorLabel);
  438.  
  439. var healthBarColorPicker = document.createElement('input');
  440. healthBarColorPicker.type = 'color';
  441. healthBarColorPicker.value = healthBarColor;
  442. healthBarColorPicker.disabled = !healthBarColorToggleState;
  443. settingsTab.appendChild(healthBarColorPicker);
  444. settingsTab.appendChild(document.createElement('br'));
  445.  
  446.  
  447. var healthBarDegradationColorLabel = document.createElement('label');
  448. healthBarDegradationColorLabel.textContent = 'Health Bar Degradation Color:';
  449. settingsTab.appendChild(healthBarDegradationColorLabel);
  450.  
  451. var healthBarDegradationColorPicker = document.createElement('input');
  452. healthBarDegradationColorPicker.type = 'color';
  453. healthBarDegradationColorPicker.value = healthBarDegradationColor;
  454. healthBarDegradationColorPicker.disabled = !healthBarColorToggleState;
  455. settingsTab.appendChild(healthBarDegradationColorPicker);
  456. settingsTab.appendChild(document.createElement('br'));
  457.  
  458.  
  459. healthBarColorToggleCheckbox.addEventListener('change', function() {
  460. healthBarColorToggleState = healthBarColorToggleCheckbox.checked;
  461. localStorage.setItem('healthBarColorToggle', healthBarColorToggleState);
  462. healthBarColorPicker.disabled = !healthBarColorToggleState;
  463. healthBarDegradationColorPicker.disabled = !healthBarColorToggleState;
  464.  
  465. if (healthBarColorToggleState) {
  466. observeHealthBar();
  467. }
  468. });
  469.  
  470. healthBarColorPicker.addEventListener('input', function() {
  471. healthBarColor = healthBarColorPicker.value;
  472. localStorage.setItem('healthBarColor', healthBarColor);
  473. });
  474.  
  475. healthBarDegradationColorPicker.addEventListener('input', function() {
  476. healthBarDegradationColor = healthBarDegradationColorPicker.value;
  477. localStorage.setItem('healthBarDegradationColor', healthBarDegradationColor);
  478. });
  479. }
  480. var gunSwitchDelayMap = {
  481. "AWM-S": 1000,
  482. "BLR 81": 1000,
  483. "Model 94": 1000,
  484. "Mosin-Nagant": 1000,
  485. "SV-98": 1000,
  486. "Scout Elite": 1000,
  487.  
  488. "Hawk 12G": 900,
  489. "Heart Cannon": 900,
  490. "M1100": 900,
  491. "M134": 900,
  492. "M79": 900,
  493. "M870": 900,
  494. "Potato Cannon": 900,
  495.  
  496. "AK-47": 750,
  497. "AN-94": 750,
  498. "BAR M1918": 750,
  499. "CZ-3A1": 750,
  500. "DP-28": 750,
  501. "FAMAS": 750,
  502. "Groza": 750,
  503. "Groza-S": 750,
  504. "L86A2": 750,
  505. "M1 Garand": 750,
  506. "M1014": 750,
  507. "M1A1": 750,
  508. "M249": 750,
  509. "M39 EMR": 750,
  510. "M416": 750,
  511. "M4A1-S": 750,
  512. "MAC-10": 750,
  513. "MP5": 750,
  514. "Mk 12 SPR": 750,
  515. "Mk45G": 750,
  516. "PKM": 750,
  517. "PKP Pecheneg": 750,
  518. "QBB-97": 750,
  519. "SCAR-H": 750,
  520. "SCAR-SSR": 750,
  521. "SPAS-12": 750,
  522. "SVD-63": 750,
  523. "Saiga-12": 750,
  524. "Spud Gun": 750,
  525. "UMP9": 750,
  526. "USAS-12": 750,
  527. "VSS": 750,
  528. "Vector": 750,
  529.  
  530. "Bugle": 300,
  531. "DEagle 50": 300,
  532. "Dual DEagle 50": 300,
  533. "Dual Flare Gun": 300,
  534. "Dual OT-38": 300,
  535. "Dual OTs-38": 300,
  536. "Dual P30L": 300,
  537. "Dual Peacemaker": 300,
  538. "Flare Gun": 300,
  539. "MP220": 300,
  540. "OT-38": 300,
  541. "OTs-38": 300,
  542. "Peacemaker": 300,
  543. "Rainbow Blaster": 300,
  544.  
  545. "Dual G18C": 250,
  546. "Dual M1911": 250,
  547. "Dual M9": 250,
  548. "Dual M93R": 250,
  549. "G18C": 250,
  550. "M1911": 250,
  551. "M9": 250,
  552. "M9 Cursed": 250,
  553. "M93R": 250,
  554. "P30L": 250
  555. };
  556.  
  557.  
  558. var currentCountdown = null;
  559.  
  560. function createSwitchDelayText() {
  561. var delayText = document.createElement('div');
  562. delayText.id = 'switch-delay-text';
  563. delayText.style.position = 'fixed';
  564. delayText.style.color = 'red';
  565. delayText.style.fontSize = '25px';
  566. delayText.style.fontWeight = 'bold';
  567. delayText.style.zIndex = '1000';
  568. delayText.style.left = '50%';
  569. delayText.style.transform = 'translateX(-50%)';
  570. delayText.style.top = '40%';
  571. document.body.appendChild(delayText);
  572. return delayText;
  573. }
  574.  
  575.  
  576. function showSwitchDelay(gunName, delayMs) {
  577. if (!switchDelayEnabled) return;
  578.  
  579. if (currentCountdown) {
  580. clearInterval(currentCountdown);
  581. }
  582.  
  583. if (!gunSwitchDelayMap[gunName]) {
  584. return;
  585. }
  586.  
  587. var delayInSeconds = (delayMs / 1000).toFixed(2);
  588. var delayText = document.getElementById('switch-delay-text') || createSwitchDelayText();
  589.  
  590. delayText.textContent = `${delayInSeconds}s`;
  591. delayText.style.display = 'block';
  592.  
  593. currentCountdown = setInterval(function() {
  594. delayInSeconds -= 0.01;
  595. delayText.textContent = `${delayInSeconds.toFixed(2)}s`;
  596.  
  597. if (delayInSeconds <= 0) {
  598. clearInterval(currentCountdown);
  599. delayText.style.display = 'none';
  600. currentCountdown = null;
  601. }
  602. }, 10);
  603. }
  604.  
  605.  
  606. function detectWeaponSwitch() {
  607. var previousWeapon = null;
  608.  
  609. setInterval(function() {
  610. var equippedWeapon = document.querySelector('.ui-weapon-switch[style*="background-color: rgba(0, 0, 0, 0.4)"], .ui-weapon-switch[style*="opacity: 1"]');
  611. if (equippedWeapon) {
  612. var weaponName = equippedWeapon.querySelector('.ui-weapon-name').textContent.trim();
  613. if (weaponName !== previousWeapon) {
  614. previousWeapon = weaponName;
  615.  
  616. var delayMs = gunSwitchDelayMap[weaponName];
  617. if (delayMs) {
  618. showSwitchDelay(weaponName, delayMs);
  619. }
  620. }
  621. }
  622. }, 100);
  623. }
  624.  
  625. detectWeaponSwitch();
  626.  
  627.  
  628. function periodicallyShowKillCounter() {
  629. showKillCounter();
  630. setTimeout(periodicallyShowKillCounter, 100);
  631. }
  632.  
  633. function showKillCounter() {
  634. var killCounter = document.getElementById('ui-kill-counter-wrapper');
  635. if (killCounter) {
  636. killCounter.style.display = 'block';
  637. killCounter.style.position = 'fixed';
  638. killCounter.style.left = '5px';
  639. killCounter.style.top = '-25px';
  640. killCounter.style.color = 'white';
  641. killCounter.style.fontSize = '20px';
  642. killCounter.style.fontWeight = 'bold';
  643. killCounter.style.zIndex = '1000';
  644. killCounter.style.backgroundColor = 'rgba(0, 0, 0, 0)';
  645. killCounter.style.padding = '5px';
  646. killCounter.style.borderRadius = '5px';
  647.  
  648. var counterText = killCounter.querySelector('.counter-text');
  649. if (counterText) {
  650. counterText.style.minWidth = '30px';
  651. }
  652. }
  653. }
  654.  
  655. function calculateAverageBoostWidth() {
  656. var counterLengths = [98.5, 98.5, 147.75, 49.25];
  657. var boostCounters = document.querySelectorAll('#ui-boost-counter .ui-bar-inner');
  658. var totalWidth = 0;
  659.  
  660. boostCounters.forEach(function(counter, index) {
  661. var widthPercentage = parseFloat(counter.style.width);
  662. var unitLength = counterLengths[index];
  663. totalWidth += (widthPercentage / 100) * unitLength;
  664. });
  665.  
  666. var totalUnitLength = counterLengths.reduce((a, b) => a + b, 0);
  667. var averageWidthPercentage = (totalWidth / totalUnitLength) * 100;
  668.  
  669. return averageWidthPercentage.toFixed(2) + "%";
  670. }
  671.  
  672. function toggleUIElementDisplay(enabled) {
  673. if (enabled) {
  674.  
  675. var healthBarWidthCopy = document.createElement('span');
  676. healthBarWidthCopy.id = 'health-bar-width-copy';
  677. healthBarWidthCopy.classList.add('unselectable');
  678. healthBarWidthCopy.style.position = 'fixed';
  679. healthBarWidthCopy.style.fontSize = '25px';
  680. healthBarWidthCopy.style.fontWeight = 'bold';
  681. healthBarWidthCopy.style.display = 'none';
  682.  
  683. var ammoCountCopy = document.createElement('span');
  684. ammoCountCopy.id = 'ammo-count-copy';
  685. ammoCountCopy.classList.add('unselectable');
  686. ammoCountCopy.style.position = 'fixed';
  687. ammoCountCopy.style.fontSize = '25px';
  688. ammoCountCopy.style.fontWeight = 'bold';
  689. ammoCountCopy.style.display = 'none';
  690.  
  691. var weaponNameCopy = document.createElement('span');
  692. weaponNameCopy.id = 'weapon-name-copy';
  693. weaponNameCopy.classList.add('unselectable');
  694. weaponNameCopy.style.position = 'fixed';
  695. weaponNameCopy.style.fontSize = '20px';
  696. weaponNameCopy.style.fontWeight = 'bold';
  697. weaponNameCopy.style.display = 'none';
  698.  
  699. var boostWidthCopy = document.createElement('span');
  700. boostWidthCopy.id = 'boost-width-copy';
  701. boostWidthCopy.classList.add('unselectable');
  702. boostWidthCopy.style.position = 'fixed';
  703. boostWidthCopy.style.fontSize = '20px';
  704. boostWidthCopy.style.fontWeight = 'bold';
  705. boostWidthCopy.style.color = 'orange';
  706. boostWidthCopy.style.display = 'none';
  707.  
  708. function updateHealthBarWidthCopy() {
  709. var healthBar = document.getElementById('ui-health-actual');
  710. if (healthBar && healthBar.offsetWidth > 0 && healthBar.offsetHeight > 0) {
  711. var healthBarWidth = Math.round(parseFloat(healthBar.style.width));
  712. var healthBarColor = healthBar.style.backgroundColor;
  713.  
  714. healthBarWidthCopy.textContent = healthBarWidth + "%";
  715. healthBarWidthCopy.style.color = healthBarColor;
  716. healthBarWidthCopy.style.display = 'block';
  717. } else {
  718. healthBarWidthCopy.style.display = 'none';
  719. }
  720. }
  721.  
  722. function updateAmmoCountCopy() {
  723. var ammoCountElement = document.getElementById('ui-current-clip');
  724.  
  725. if (ammoCountElement && window.getComputedStyle(ammoCountElement).display !== 'none' && parseFloat(window.getComputedStyle(ammoCountElement).opacity) > 0) {
  726. var ammoCount = ammoCountElement.textContent;
  727. ammoCountCopy.textContent = ammoCount;
  728. ammoCountCopy.style.color = ammoCountElement.style.color;
  729. ammoCountCopy.style.display = 'block';
  730. } else {
  731. ammoCountCopy.style.display = 'none';
  732. }
  733. }
  734.  
  735. function updateWeaponNameCopy() {
  736. var equippedWeapon = document.querySelector('.ui-weapon-switch[style*="background-color: rgba(0, 0, 0, 0.4)"], .ui-weapon-switch[style*="opacity: 1"]');
  737. if (equippedWeapon) {
  738. var weaponName = equippedWeapon.querySelector('.ui-weapon-name').textContent;
  739. weaponNameCopy.textContent = weaponName;
  740. weaponNameCopy.style.color = 'white';
  741. weaponNameCopy.style.display = 'block';
  742. } else {
  743. weaponNameCopy.style.display = 'none';
  744. }
  745. }
  746.  
  747. function updateBoostWidthCopy() {
  748. var boostElement = document.getElementById('ui-boost-counter');
  749. if (boostElement && window.getComputedStyle(boostElement).display !== 'none' && parseFloat(window.getComputedStyle(boostElement).opacity) > 0) {
  750. var averageBoostWidth = calculateAverageBoostWidth();
  751. boostWidthCopy.textContent = averageBoostWidth;
  752. boostWidthCopy.style.display = 'block';
  753. } else {
  754. boostWidthCopy.style.display = 'none';
  755. }
  756. }
  757.  
  758. function followCursor(event) {
  759. healthBarWidthCopy.style.left = `${event.clientX - 70}px`;
  760. healthBarWidthCopy.style.top = `${event.clientY + 25}px`;
  761.  
  762. ammoCountCopy.style.left = `${event.clientX + 40}px`;
  763. ammoCountCopy.style.top = `${event.clientY + 25}px`;
  764.  
  765. weaponNameCopy.style.left = `${event.clientX + 40}px`;
  766. weaponNameCopy.style.top = `${event.clientY + 50}px`;
  767.  
  768. boostWidthCopy.style.left = `${event.clientX - 70}px`;
  769. boostWidthCopy.style.top = `${event.clientY + 50}px`;
  770. }
  771. document.addEventListener('mousemove', followCursor);
  772.  
  773. healthBarWidthCopy.style.webkitTouchCallout = 'none'; /* iOS Safari */
  774. healthBarWidthCopy.style.webkitUserSelect = 'none'; /* Safari */
  775. healthBarWidthCopy.style.userSelect = 'none'; /* Standard syntax */
  776.  
  777. ammoCountCopy.style.webkitTouchCallout = 'none'; /* iOS Safari */
  778. ammoCountCopy.style.webkitUserSelect = 'none'; /* Safari */
  779. ammoCountCopy.style.userSelect = 'none'; /* Standard syntax */
  780.  
  781. weaponNameCopy.style.webkitTouchCallout = 'none'; /* iOS Safari */
  782. weaponNameCopy.style.webkitUserSelect = 'none'; /* Safari */
  783. weaponNameCopy.style.userSelect = 'none'; /* Standard syntax */
  784.  
  785. boostWidthCopy.style.webkitTouchCallout = 'none'; /* iOS Safari */
  786. boostWidthCopy.style.webkitUserSelect = 'none'; /* Safari */
  787. boostWidthCopy.style.userSelect = 'none'; /* Standard syntax */
  788.  
  789. document.body.appendChild(healthBarWidthCopy);
  790. document.body.appendChild(ammoCountCopy);
  791. document.body.appendChild(weaponNameCopy);
  792. document.body.appendChild(boostWidthCopy);
  793.  
  794. updateHealthBarWidthCopy();
  795. updateAmmoCountCopy();
  796. updateWeaponNameCopy();
  797. updateBoostWidthCopy();
  798.  
  799. var healthObserver = new MutationObserver(updateHealthBarWidthCopy);
  800. var healthTargetNode = document.getElementById('ui-health-actual');
  801. if (healthTargetNode) {
  802. healthObserver.observe(healthTargetNode, { attributes: true, attributeFilter: ['style', 'class'] });
  803. }
  804. if (healthTargetNode && healthTargetNode.parentElement) {
  805. healthObserver.observe(healthTargetNode.parentElement, { attributes: true, attributeFilter: ['style', 'class'] });
  806. }
  807.  
  808. var ammoObserver = new MutationObserver(updateAmmoCountCopy);
  809. var ammoTargetNode = document.getElementById('ui-current-clip');
  810. if (ammoTargetNode) {
  811. ammoObserver.observe(ammoTargetNode, { attributes: true, childList: true, subtree: true });
  812. }
  813.  
  814. var weaponObserver = new MutationObserver(updateWeaponNameCopy);
  815. var weaponTargetNodes = document.querySelectorAll('.ui-weapon-switch');
  816. weaponTargetNodes.forEach(function(node) {
  817. weaponObserver.observe(node, { attributes: true, attributeFilter: ['style', 'class'] });
  818. });
  819.  
  820. var boostObserver = new MutationObserver(updateBoostWidthCopy);
  821. var boostTargetNodes = document.querySelectorAll('#ui-boost-counter .ui-bar-inner');
  822. boostTargetNodes.forEach(function(node) {
  823. boostObserver.observe(node, { attributes: true, attributeFilter: ['style', 'class'] });
  824. });
  825.  
  826. } else {
  827. var healthBarWidthCopy = document.getElementById('health-bar-width-copy');
  828. if (healthBarWidthCopy) {
  829. healthBarWidthCopy.parentNode.removeChild(healthBarWidthCopy);
  830. }
  831.  
  832. var ammoCountCopy = document.getElementById('ammo-count-copy');
  833. if (ammoCountCopy) {
  834. ammoCountCopy.parentNode.removeChild(ammoCountCopy);
  835. }
  836.  
  837. var weaponNameCopy = document.getElementById('weapon-name-copy');
  838. if (weaponNameCopy) {
  839. weaponNameCopy.parentNode.removeChild(weaponNameCopy);
  840. }
  841.  
  842. var boostWidthCopy = document.getElementById('boost-width-copy');
  843. if (boostWidthCopy) {
  844. boostWidthCopy.parentNode.removeChild(boostWidthCopy);
  845. }
  846. }
  847. }
  848.  
  849. toggleUIElementDisplay(true);
  850. showKillCounter();
  851. periodicallyShowKillCounter();
  852. })();