Displays real-time rendering engine, FPS, and performance degradation decisions made by the battle client.
// ==UserScript==
// @name Battle Engine Performance Debugger
// @namespace http://tampermonkey.net/
// @version 1.4
// @description Displays real-time rendering engine, FPS, and performance degradation decisions made by the battle client.
// @match *://*.heroeswm.ru/*
// @match *://*.lordswm.com/*
// @grant none
// @run-at document-end
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const panel = document.createElement('div');
panel.id = 'battle-debug-panel';
panel.innerHTML = `
<div id="debug-header">
<span>⚙️ Battle Debug</span>
<span id="debug-toggle" title="Minimize/Maximize">−</span>
</div>
<div id="debug-content">
<div class="debug-row"><span class="label">Engine:</span> <span id="d-engine" class="value">Loading...</span></div>
<div class="debug-row"><span class="label">Avg FPS:</span> <span id="d-fps" class="value">--</span></div>
<div class="debug-row"><span class="label">Slow FPS Mode:</span> <span id="d-slowfps" class="value">--</span></div>
<div class="debug-row"><span class="label">Shadows:</span> <span id="d-shadows" class="value">--</span></div>
<div class="debug-row"><span class="label">Pixel Ratio:</span> <span id="d-pixel" class="value">--</span></div>
<div class="debug-row"><span class="label">Idle Anim Limit:</span> <span id="d-freecnt" class="value">--</span></div>
<div class="debug-row"><span class="label">WebGL Failed:</span> <span id="d-webgl" class="value">--</span></div>
<div class="debug-row"><span class="label">Android HiRes:</span> <span id="d-hires" class="value">--</span></div>
<div class="debug-row"><span class="label">Engine Code:</span> <span id="d-code" class="value">--</span></div>
<div class="debug-row"><span class="label">Stage Size:</span> <span id="d-size" class="value">--</span></div>
</div>
`;
const style = document.createElement('style');
style.textContent = `
#battle-debug-panel {
position: fixed;
top: 10px;
right: 10px;
width: 240px;
background: rgba(15,15,15,.92);
color: #e0e0e0;
font-family: Consolas, Monaco, monospace;
font-size: 11px;
border: 1px solid #444;
border-radius: 6px;
z-index: 999999;
box-shadow: 0 4px 15px rgba(0,0,0,.5);
}
/* Force selection inside panel */
#battle-debug-panel,
#battle-debug-panel *,
#debug-content,
#debug-content *,
.debug-row,
.debug-row *,
.label,
.value {
user-select: text !important;
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
}
/* Except header */
#debug-header,
#debug-header * {
user-select: none !important;
-webkit-user-select: none !important;
-moz-user-select: none !important;
-ms-user-select: none !important;
}
#debug-header {
background: #2a2a2a;
padding: 6px 10px;
border-bottom: 1px solid #444;
border-radius: 5px 5px 0 0;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
}
#debug-toggle {
cursor: pointer;
color: #aaa;
font-size: 14px;
}
#debug-toggle:hover {
color: #fff;
}
#debug-content {
padding: 8px 10px;
overflow: hidden;
transition: max-height .25s ease, opacity .25s ease;
}
#debug-content.minimized {
max-height: 0;
padding: 0 10px;
opacity: 0;
}
.debug-row {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
line-height: 1.4;
}
.label {
color: #888;
}
.value {
color: #0f0;
font-weight: bold;
text-align: right;
}
.value.good { color: #00ff88; }
.value.warn { color: #ffaa00; }
.value.bad { color: #ff4444; }
`;
document.head.appendChild(style);
document.body.appendChild(panel);
// Dragging
const header = document.getElementById('debug-header');
let isDragging = false;
let startX = 0;
let startY = 0;
let initialLeft = 0;
let initialTop = 0;
header.addEventListener('mousedown', (e) => {
if (e.target.id === 'debug-toggle') return;
if (e.button !== 0) return;
isDragging = true;
const rect = panel.getBoundingClientRect();
startX = e.clientX;
startY = e.clientY;
initialLeft = rect.left;
initialTop = rect.top;
panel.style.left = rect.left + 'px';
panel.style.top = rect.top + 'px';
panel.style.right = 'auto';
header.style.cursor = 'grabbing';
});
window.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
panel.style.left = (initialLeft + dx) + 'px';
panel.style.top = (initialTop + dy) + 'px';
});
window.addEventListener('mouseup', () => {
isDragging = false;
header.style.cursor = 'move';
});
// Minimize
document.getElementById('debug-toggle').addEventListener('click', () => {
const content = document.getElementById('debug-content');
const toggle = document.getElementById('debug-toggle');
content.classList.toggle('minimized');
toggle.textContent =
content.classList.contains('minimized') ? '+' : '−';
});
// Update loop
setInterval(() => {
const w = window;
const engineEl = document.getElementById('d-engine');
if (w.currentTip === 1) {
engineEl.textContent = 'PIXI (WebGL)';
engineEl.className = 'value good';
} else if (w.currentTip === 0) {
engineEl.textContent = 'Konva (Canvas)';
engineEl.className = 'value warn';
} else {
engineEl.textContent = 'Initializing...';
engineEl.className = 'value';
}
const fpsEl = document.getElementById('d-fps');
let fpsText = 'N/A';
let fpsValue = NaN;
if (typeof w.finial_fps === 'number' && w.finial_fps > 0) {
fpsText = w.finial_fps.toFixed(1);
fpsValue = w.finial_fps;
} else if (typeof w.fps_long === 'number' && w.fps_long > 0) {
fpsText = `~${w.fps_long} (Test)`;
fpsValue = w.fps_long;
}
fpsEl.textContent = fpsText;
if (!isNaN(fpsValue)) {
fpsEl.className =
fpsValue < 30 ? 'value bad' : 'value good';
}
const slowEl = document.getElementById('d-slowfps');
slowEl.textContent = w.slow_fps ? 'YES (Degraded)' : 'NO';
slowEl.className = w.slow_fps ? 'value bad' : 'value good';
const shadowEl = document.getElementById('d-shadows');
shadowEl.textContent = w.is_shadow ? 'ON' : 'OFF';
shadowEl.className = w.is_shadow ? 'value good' : 'value warn';
document.getElementById('d-pixel').textContent =
w.MainPixelRatio ?? '1.0';
document.getElementById('d-freecnt').textContent =
w.free_cnt_max ?? 'N/A';
const webglEl = document.getElementById('d-webgl');
if (w.no_webgl) {
webglEl.textContent =
`YES (Code: ${w.webgl_fail_code ?? '?'})`;
webglEl.className = 'value bad';
} else {
webglEl.textContent = 'NO';
webglEl.className = 'value good';
}
const hiresEl = document.getElementById('d-hires');
hiresEl.textContent = w.android_hires ? 'YES' : 'NO';
hiresEl.className = w.android_hires ? 'value good' : 'value warn';
document.getElementById('d-code').textContent =
w.setted_current_engine ?? 'N/A';
const sizeEl = document.getElementById('d-size');
if (w.stage_width && w.stage_height) {
sizeEl.textContent =
Math.round(w.stage_width) + 'x' +
Math.round(w.stage_height);
} else {
sizeEl.textContent = 'N/A';
}
}, 500);
})();