Track EXP gain, rate, and ETA Level Up for Darkest Age
当前为
// ==UserScript==
// @license MIT
// @name Darkest Age EXP Tracker
// @namespace darkestage-exp-tracker
// @version 1.1.0
// @description Track EXP gain, rate, and ETA Level Up for Darkest Age
// @match https://darkestage.net/game/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
let totalGainedExp = 0;
let lastExp = null;
let startTime = Date.now();
let maxExp = 0;
let paused = false;
// ===== HIDE NUMBER INPUT ARROWS =====
const style = document.createElement("style");
style.textContent = `
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type=number] {
-moz-appearance: textfield;
}
`;
document.head.appendChild(style);
// ===== WAIT UNTIL EXP ELEMENT EXISTS =====
function waitForExpElement() {
const el = document.querySelector(".exp[data-value]");
if (!el) {
setTimeout(waitForExpElement, 1000);
return;
}
init();
}
function init() {
// ===== UI =====
const ui = document.createElement("div");
ui.style.cssText = `
position: fixed;
top: 120px;
left: 20px;
width: 290px;
background: rgba(0,0,0,0.9);
color: #00ff9c;
font-family: Arial, sans-serif;
font-size: 13px;
padding: 10px;
border-radius: 8px;
z-index: 999999;
cursor: move;
`;
ui.innerHTML = `
<b>EXP Tracker</b><br>
<hr style="border:1px solid #333">
Max EXP:<br>
<input id="max-exp-input" type="number" placeholder="contoh: 808000"
style="width:100%;margin-bottom:6px;background:#111;color:#0f0;border:1px solid #333;padding:4px;border-radius:4px">
<div style="margin-bottom:6px">
<button id="pause-btn">Pause</button>
<button id="reset-btn">Reset</button>
</div>
Gained EXP: <span id="gx">0</span><br>
Current EXP: <span id="cx">0</span><br>
Need EXP: <span id="nx">-</span><br>
Progress: <span id="px">0%</span><br>
ETA Level Up: <span id="eta">--:--:--</span>
<hr style="border:1px solid #333">
Time: <span id="gt">00:00</span><br>
EXP/min: <span id="gm">0</span><br>
EXP/hour: <span id="gh">0</span>
`;
document.body.appendChild(ui);
// ===== BUTTON STYLE =====
ui.querySelectorAll("button").forEach(btn => {
btn.style.cssText = `
background:#111;
color:#0f0;
border:1px solid #333;
padding:4px 8px;
border-radius:4px;
cursor:pointer;
margin-right:4px;
`;
});
// ===== DRAG UI =====
let drag = false, ox = 0, oy = 0;
ui.onmousedown = e => {
drag = true;
ox = e.clientX - ui.offsetLeft;
oy = e.clientY - ui.offsetTop;
};
document.onmousemove = e => {
if (!drag) return;
ui.style.left = e.clientX - ox + "px";
ui.style.top = e.clientY - oy + "px";
};
document.onmouseup = () => drag = false;
// ===== READ EXP =====
function getExp() {
const el = document.querySelector(".exp[data-value]");
if (!el) return null;
return parseInt(el.getAttribute("data-value"), 10);
}
// ===== INIT VALUES =====
lastExp = getExp();
document.getElementById("cx").textContent = lastExp;
// ===== MAX EXP INPUT (SAVE) =====
const maxInput = document.getElementById("max-exp-input");
maxInput.value = localStorage.getItem("da_max_exp") || "";
maxExp = parseInt(maxInput.value, 10) || 0;
maxInput.addEventListener("change", () => {
maxExp = parseInt(maxInput.value, 10) || 0;
localStorage.setItem("da_max_exp", maxExp);
});
// ===== PAUSE =====
document.getElementById("pause-btn").onclick = e => {
paused = !paused;
e.target.textContent = paused ? "Resume" : "Pause";
if (!paused) lastExp = getExp();
};
// ===== RESET =====
document.getElementById("reset-btn").onclick = () => {
totalGainedExp = 0;
startTime = Date.now();
lastExp = getExp();
document.getElementById("eta").textContent = "--:--:--";
};
// ===== MAIN LOOP =====
setInterval(() => {
const current = getExp();
if (current === null) return;
document.getElementById("cx").textContent = current;
if (!paused && current > lastExp) {
totalGainedExp += current - lastExp;
}
lastExp = current;
const elapsed = Date.now() - startTime;
const min = elapsed / 60000;
const hour = elapsed / 3600000;
const expPerMin = min ? totalGainedExp / min : 0;
document.getElementById("gx").textContent = totalGainedExp;
document.getElementById("gm").textContent = Math.floor(expPerMin);
document.getElementById("gh").textContent =
hour ? Math.floor(totalGainedExp / hour) : 0;
if (maxExp > 0) {
const need = Math.max(0, maxExp - current);
const progress = Math.min(100, (current / maxExp) * 100);
document.getElementById("nx").textContent = need;
document.getElementById("px").textContent = progress.toFixed(2) + "%";
if (expPerMin > 0 && !paused && need > 0) {
const etaSec = Math.floor((need / expPerMin) * 60);
const h = Math.floor(etaSec / 3600);
const m = Math.floor((etaSec % 3600) / 60);
const s = etaSec % 60;
document.getElementById("eta").textContent =
String(h).padStart(2, "0") + ":" +
String(m).padStart(2, "0") + ":" +
String(s).padStart(2, "0");
} else {
document.getElementById("eta").textContent = "--:--:--";
}
}
const tmin = Math.floor(elapsed / 60000);
const ts = Math.floor((elapsed % 60000) / 1000);
document.getElementById("gt").textContent =
String(tmin).padStart(2, "0") + ":" + String(ts).padStart(2, "0");
}, 200);
console.log("✅ Darkest Age EXP Tracker aktif");
}
waitForExpElement();
})();