Displays an estimated fractional level based on Hall of Fame position.
Od
// ==UserScript==
// @name Level Progress Estimation
// @namespace http://tampermonkey.net/
// @version 11.1111
// @description Displays an estimated fractional level based on Hall of Fame position.
// @author Pint-Shot-Riot
// @match https://www.torn.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @connect api.torn.com
// @license MIT
// ==/UserScript==
(function () {
"use strict";
async function getApiKey() {
let key = localStorage.getItem("APIKey") || await GM_getValue("torn_api_key", "");
if (!key || key.length < 10) {
key = prompt("Please enter your Torn API Key:");
if (key) await GM_setValue("torn_api_key", key.trim());
}
return key ? key.trim() : null;
}
async function fetchTorn(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: (res) => {
try {
const data = JSON.parse(res.responseText);
if (data.error) reject(data.error.error);
else resolve(data);
} catch (e) { reject("JSON Error"); }
},
onerror: (err) => reject(err)
});
});
}
async function getAccurateLevel() {
const key = await getApiKey();
if (!key) return null;
try {
const userHof = await fetchTorn(`https://api.torn.com/v2/user/hof?key=${key}`);
const level = userHof.hof.level.value;
const myRank = userHof.hof.level.rank;
if (level >= 100) return "100.00";
const offset = Math.max(0, myRank - 250);
const hofData = await fetchTorn(`https://api.torn.com/v2/torn/hof?limit=500&offset=${offset}&cat=level&key=${key}`);
const players = hofData.hof || [];
const currentLevel = players.filter(p => p.level === level).map(p => p.position);
const prevLevel = players.filter(p => p.level === (level - 1)).map(p => p.position);
if (currentLevel.length === 0) return level.toFixed(2);
const topOfLevel = Math.min(...currentLevel);
let bottomOfLevel = prevLevel.length > 0 ? Math.min(...prevLevel) : Math.max(...currentLevel) + 1;
const range = bottomOfLevel - topOfLevel;
const progress = bottomOfLevel - myRank;
let fraction = progress / range;
if (fraction <= 0) fraction = 0.01;
if (fraction >= 1) fraction = 0.99;
return (level + fraction).toFixed(2);
} catch (e) {
console.error(e);
return null;
}
}
function injectIcon(val) {
let pill = document.getElementById('acc-lvl-pill');
if (pill) {
document.getElementById('acc-lvl-val').textContent = val;
return;
}
const container = document.querySelector('[class*="header-navigation_"]') || document.querySelector('[class*="header-wrapper_"]');
if (!container) return;
container.style.display = 'flex';
container.style.justifyContent = 'center';
container.style.alignItems = 'center';
pill = document.createElement('div');
pill.id = 'acc-lvl-pill';
pill.style = "display: inline-flex; align-items: center; background: #333; border: 1px solid #444; border-radius: 10px; padding: 2px 8px; height: 22px; cursor: pointer; z-index: 9999; margin: 0 auto; order: 5;";
pill.innerHTML = `<span style="color: #85b200; font-size: 10px; font-weight: bold; margin-right: 4px; font-family: sans-serif;">LV</span><span id="acc-lvl-val" style="color: #fff; font-size: 11px; font-family: 'Courier New', monospace; font-weight: bold;">${val}</span>`;
pill.onclick = (e) => { e.preventDefault(); window.location.href = "/halloffame.php#/type=level"; };
container.appendChild(pill);
}
async function refresh() {
const val = await getAccurateLevel();
if (val) injectIcon(val);
}
async function run() {
await refresh();
setInterval(refresh, 600000);
setInterval(() => { if (!document.getElementById('acc-lvl-pill')) injectIcon(window.lastLvlVal || "67.00"); }, 2000);
const observer = new MutationObserver(() => refresh());
const sidebar = document.querySelector('#sidebarroot');
if (sidebar) observer.observe(sidebar, { subtree: true, characterData: true, childList: true });
}
if (document.readyState === "complete") run();
else window.addEventListener("load", run);
})();