您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a toggle-able side bar to estimate War End for a given faction.
// ==UserScript== // @name War End // @version Beta1.6 // @namespace https://greasyfork.org/ // @description Adds a toggle-able side bar to estimate War End for a given faction. // @author Gravity0000 // @supportURL https://www.torn.com/profiles.php?XID=2131364 // @license MIT // @match https://www.torn.com/* // @grant GM_addStyle // ==/UserScript== (function () { 'use strict'; const styleTag = document.createElement('style'); document.head.appendChild(styleTag); const isDarkMode = document.body.classList.contains('dark-mode'); let placeholderColor = isDarkMode ? '#bbbbbb' : '#555555'; function updatePlaceholderStyle() { styleTag.textContent = ` .leadBox::placeholder { color: ${placeholderColor} !important; } `; } const container = document.createElement('div'); container.id = 'myScriptContainer'; container.style.display = 'none'; document.body.appendChild(container); const infoBox = document.createElement('div'); infoBox.id = 'myInfoBox'; infoBox.innerHTML = 'Initial Info'; container.appendChild(infoBox); const leadBox = document.createElement('input'); leadBox.type = 'number'; leadBox.id = 'myLeadBox'; leadBox.placeholder = 'Est. Lead or Blank'; leadBox.classList.add('leadBox'); container.appendChild(leadBox); leadBox.addEventListener('keypress', event => { const key = event.keyCode || event.which; if (!((key >= 48 && key <= 57) || key === 8)) event.preventDefault(); }); const toggleButton = document.createElement('button'); toggleButton.innerText = 'WarEnd'; toggleButton.id = 'myToggleButton'; Object.assign(toggleButton.style, { position: 'fixed', top: '65%', right: '0%', zIndex: '9999', cursor: 'pointer' }); document.body.appendChild(toggleButton); toggleButton.addEventListener('click', () => { container.style.display = container.style.display === 'none' ? '' : 'none'; }); const updateButton = document.createElement('button'); updateButton.id = 'myUpdateButton'; updateButton.textContent = 'Update'; container.appendChild(updateButton); function applyThemeColors(isDark) { const textColor = isDark ? 'white' : 'black'; const bgColor = isDark ? 'black' : 'white'; const containerBg = isDark ? '#A9A9A9' : '#D3D3D3'; const buttonBg = isDark ? '#009407' : '#98f59b'; const borderColor = isDark ? 'white' : 'black'; [infoBox, leadBox, toggleButton, updateButton].forEach(el => el.style.color = textColor); [infoBox, leadBox].forEach(el => el.style.backgroundColor = bgColor); [toggleButton, updateButton].forEach(el => { el.style.border = `1px solid ${borderColor}`; el.style.backgroundColor = buttonBg; }); container.style.backgroundColor = containerBg; placeholderColor = isDark ? '#FFFFFF' : '#000000'; updatePlaceholderStyle(); } const observer = new MutationObserver(() => { applyThemeColors(document.body.classList.contains('dark-mode')); }); observer.observe(document.body, { attributes: true, attributeFilter: ['class'] }); updateButton.addEventListener('click', () => { const leadInput = parseInt(leadBox.value, 10); const noWar = document.querySelector('.f-msg .title')?.textContent.includes('NOT IN A WAR'); const timerElem = document.querySelector('.infoBlock___bb_KF.timer___fSGg8'); if (noWar && !timerElem) { infoBox.innerHTML = 'No current war'; return; } if (!noWar && timerElem) { const spans = timerElem.querySelectorAll('span'); const days = parseInt(spans[0].textContent + spans[1].textContent, 10); const hours = parseInt(spans[3].textContent + spans[4].textContent, 10); const minutes = parseInt(spans[6].textContent + spans[7].textContent, 10); const seconds = parseInt(spans[9].textContent + spans[10].textContent, 10); const targetElem = document.querySelector('.scoreBlock___Pr3xV .target___NBVXq'); const scoreElem = document.querySelector('.scoreBlock___Pr3xV .right.scoreText___uVRQm.currentFaction___Omz6o'); if (!targetElem || !scoreElem) { infoBox.innerHTML = 'War not started'; return; } const match = targetElem.textContent.match(/(\d{1,3}(?:,\d{3})*|\d+)\s*\/\s*(\d{1,3}(?:,\d{3})*|\d+)/); if (!match) { infoBox.innerHTML = 'Unable to parse target lead'; return; } const currentLead = parseInt(match[1].replace(/,/g, ''), 10); const targetLead = parseInt(match[2].replace(/,/g, ''), 10); const hypotheticalLead = !isNaN(leadInput) ? leadInput : currentLead; const elapsedMs = (((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000; const nowMs = Date.now(); const warStartMs = nowMs - elapsedMs; const elapsedHoursTotal = days * 24 + hours + minutes / 60 + seconds / 3600; const scoreGap = Math.max(0, targetLead - hypotheticalLead); let timeLeftHours = 0; if (scoreGap <= 0) { timeLeftHours = 0; } else { if (elapsedHoursTotal < 24) { // Decay has not started yet, first tick at 24h const decayPerHour = targetLead / 100; // fixed for first 100 ticks const hoursUntilFirstTick = 24 - elapsedHoursTotal; const remainingScore = scoreGap; const additionalHours = Math.ceil(remainingScore / decayPerHour); timeLeftHours = hoursUntilFirstTick + additionalHours; } else { // Decay has started const fullElapsedHours = Math.floor(elapsedHoursTotal); const remainingDecayHours = 123 - fullElapsedHours; const decayPerHour = targetLead / remainingDecayHours; timeLeftHours = Math.ceil(scoreGap / decayPerHour) - (elapsedHoursTotal - fullElapsedHours); } } const dispDays = Math.floor(timeLeftHours / 24); const dispHours = Math.floor(timeLeftHours % 24); const dispMins = Math.round((timeLeftHours % 1) * 60); let timeLeftStr; if (dispDays > 0) { timeLeftStr = `${dispDays}d ${dispHours}h ${dispMins}m`; } else if (dispHours > 0) { timeLeftStr = `${dispHours}h ${dispMins}m`; } else { timeLeftStr = `${dispMins}m`; } const endMs = nowMs + timeLeftHours * 3600000; const endDate = new Date(endMs); // Round up to next full minute if (endDate.getSeconds() > 0 || endDate.getMilliseconds() > 0) { endDate.setMinutes(endDate.getMinutes() + 1); } endDate.setSeconds(0, 0); const endHours = String(endDate.getUTCHours()).padStart(2, '0'); const endMinutes = String(endDate.getUTCMinutes()).padStart(2, '0'); let tctStr = `End Time TCT:<br>${endHours}:${endMinutes}`; if (dispDays > 0) tctStr = `End Time TCT:<br>In ${dispDays}D at ${endHours}:${endMinutes}`; infoBox.innerHTML = `Time Left:<br>${timeLeftStr}<br><br>${tctStr}`; } else { infoBox.innerHTML = 'War not started'; } }); GM_addStyle(` #myScriptContainer { position: fixed; width: 150px; top: 70%; right: 0%; background-color: #f0f0f0; border: 1px solid #ccc; padding: 10px; z-index: 9999; display: flex; flex-direction: column; gap: 5px; } #myLeadBox { padding: 5px; border: 1px solid #ddd; } #myInfoBox { color: black; padding: 5px; text-align: center; background-color: #f2f2f2; } #myUpdateButton, #myToggleButton { padding: 5px 10px; cursor: pointer; } `); // Apply initial theme applyThemeColors(isDarkMode); })();