Evil UI made by shamans used by shamans. - With felis help, pickle and mutant
// ==UserScript==
// @name SHAM UI
// @namespace https://greasyfork.org/users/paozzh
// @match https://hordes.io/play
// @icon https://hordes.io/data/ui/classes/3.avif
// @grant none
// @version 1.2.1
// @description Evil UI made by shamans used by shamans. - With felis help, pickle and mutant
// @author Paozzh
// ==/UserScript==
(function() {
'use strict';
const AUTO_CC_ICONS = ["stunBuff.avif", "deepFrozen.avif", "root.avif", "14.avif", "37.avif", "49.avif", "50.avif"];
const MANAGED_BUFFS = {
"25.avif": "Temp",
"19.avif": "WC",
"20.avif": "Crus",
"22.avif": "Aura",
"24.avif": "Ench",
"18.avif": "Bleed",
"29.avif": "Poison",
"obeliskbuff.avif": "Obelisk Buff"
};
const IMPORTANT_BUFFS = {
"11.avif": "Invig",
"27.avif": "Pathfind",
"31.avif": "Swift",
"38.avif": "Dash",
"16.avif": "Hypo",
"extraBolt.avif": "Bolt Passive",
"13.avif": "Mimirs",
"17.avif": "Enrage"
};
const DEFAULTS = {
isEnabled: true,
mouseOverTarget: false,
hpThreshold: 80,
pulseSpeed: 0.3,
pulseStrength: 0.9,
borderSize: 2,
activeCCs: { "25.avif": true, "19.avif": true, "20.avif": true, "22.avif": true, "24.avif": true, "18.avif": true, "29.avif": true, "obeliskbuff.avif": true },
activeImportants: { "11.avif": true, "27.avif": true, "31.avif": true, "38.avif": true, "16.avif": true, "extraBolt.avif": true, "13.avif": true, "17.avif": true }
};
let config = JSON.parse(localStorage.getItem('shamConfig')) || DEFAULTS;
const style = document.createElement('style');
document.head.appendChild(style);
function updateStyles() {
const str = config.pulseStrength || 0.9;
const bdr = config.borderSize || 2;
style.innerHTML = `
@keyframes shamRedPulse { 0% { background-color: rgba(255,0,0,${str * 0.2}); } 50% { background-color: rgba(255,0,0,${str}); } 100% { background-color: rgba(255,0,0,${str * 0.2}); } }
@keyframes shamYellowPulse { 0% { background-color: rgba(255,255,0,${str * 0.2}); } 50% { background-color: rgba(255,255,0,${str}); } 100% { background-color: rgba(255,255,0,${str * 0.2}); } }
div[class*="panel-black"][class*="barsInner"].hp-panic-active {
animation: shamRedPulse ${config.pulseSpeed}s infinite ease-in-out !important;
border: ${bdr}px solid red !important;
box-shadow: 0 0 20px rgba(255,0,0,0.9) !important;
background-image: none !important;
}
div[class*="panel-black"][class*="barsInner"].cc-active-pulse {
animation: shamYellowPulse ${config.pulseSpeed}s infinite ease-in-out !important;
border: ${bdr}px solid yellow !important;
box-shadow: 0 0 20px rgba(255,255,0,0.9) !important;
background-image: none !important;
}
.sham-panel {
position: fixed;
width: 310px;
height: 500px;
background: rgba(8, 4, 16, 0.98);
backdrop-filter: blur(12px);
border: 1px solid #3c2a5c;
border-top: 3px solid #9d4edd;
border-radius: 8px;
box-shadow: 0 20px 60px rgba(0,0,0,0.95);
color: #eee;
font-family: 'Segoe UI', Tahoma, sans-serif;
z-index: 1000000;
display: none;
flex-direction: column;
user-select: none;
opacity: 0;
transform: scale(0.95);
transition: opacity 0.25s ease, transform 0.25s ease;
}
.sham-panel.active {
opacity: 1;
transform: scale(1);
}
.sham-header {
flex: 0 0 auto;
padding: 12px 18px 10px;
border-bottom: 1px solid #2a1b3d;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
}
.header-left {
display: flex;
align-items: center;
gap: 10px;
}
.header-left img {
width: 30px;
height: 30px;
border-radius: 6px;
border: 1px solid #9d4edd;
box-shadow: 0 0 10px rgba(157, 78, 221, 0.4);
}
.sham-header h3 {
margin: 0;
color: #e0aaff;
font-size: 20px;
text-shadow: 0 0 12px rgba(157, 78, 221, 0.5);
letter-spacing: 1px;
}
.sham-close {
font-size: 28px;
font-weight: bold;
cursor: pointer;
color: #baa0ff;
transition: all 0.3s ease;
}
.sham-close:hover {
color: #e0aaff;
transform: scale(1.2) rotate(90deg);
}
.sham-content {
flex: 1;
padding: 14px 18px;
display: flex;
flex-direction: column;
gap: 12px;
overflow: hidden;
}
.sham-desc {
font-size: 11px;
color: #c8b6ff;
line-height: 1.5;
background: rgba(157, 78, 221, 0.08);
padding: 9px;
border-radius: 6px;
border-left: 2px solid #9d4edd;
}
.sham-warning {
font-size: 10px;
color: #ffaaaa;
font-weight: bold;
text-align: center;
background: rgba(255, 100, 100, 0.15);
padding: 6px;
border-radius: 6px;
border: 1px solid #ff6666;
margin-bottom: 8px;
line-height: 1.3;
}
.sham-list {
flex: 1;
overflow-y: auto;
padding-right: 5px;
}
.sham-list::-webkit-scrollbar { width: 6px; }
.sham-list::-webkit-scrollbar-track { background: #1a1226; border-radius: 3px; }
.sham-list::-webkit-scrollbar-thumb { background: #5a189a; border-radius: 3px; }
.sham-list::-webkit-scrollbar-thumb:hover { background: #9d4edd; }
.toggle-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 7px 10px;
font-size: 13px;
color: #ddd;
border-radius: 6px;
transition: background 0.3s ease;
}
.toggle-row:hover {
background: rgba(157, 78, 221, 0.12);
}
.toggle-row.enabled {
background: rgba(157, 78, 221, 0.28);
}
.toggle-row.enabled .module-name {
color: #e0aaff;
}
.toggle-label {
display: flex;
align-items: center;
flex: 1;
}
.module-name {
font-weight: 600;
}
.toggle-switch {
position: relative;
width: 38px;
height: 20px;
}
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.slider {
position: absolute;
cursor: pointer;
top: 0; left: 0; right: 0; bottom: 0;
background-color: #1a1226;
transition: .4s;
border-radius: 20px;
border: 1px solid #3c2a5c;
}
.slider:before {
position: absolute;
content: "";
height: 14px;
width: 14px;
left: 3px;
bottom: 3px;
background-color: #5a189a;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider { background-color: #3c096c; border-color: #9d4edd; }
input:checked + .slider:before { transform: translateX(18px); background-color: #ffffff; box-shadow: 0 0 8px #9d4edd; }
.sham-label {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #b79ced;
font-weight: 600;
}
input[type=range] {
width: 100%;
background: #1a1226;
height: 5px;
-webkit-appearance: none;
border-radius: 8px;
outline: none;
margin: 7px 0;
}
input[type=range]::-webkit-slider-runnable-track {
background: linear-gradient(90deg, #5a189a, #9d4edd);
border-radius: 8px;
height: 5px;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 15px;
height: 15px;
background: #ffffff;
border: 2px solid #9d4edd;
border-radius: 50%;
cursor: pointer;
margin-top: -5px;
box-shadow: 0 0 8px rgba(157, 78, 221, 0.8);
}
.sham-btn {
width: 100%;
padding: 12px;
font-weight: 800;
border-radius: 6px;
border: none;
cursor: pointer;
background: #9d4edd;
color: #fff;
margin-top: auto;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 1.1px;
font-size: 13px;
}
.sham-btn:hover {
background: #e0aaff;
color: #240046;
box-shadow: 0 0 20px rgba(157, 78, 221, 0.5);
}
.stagger {
opacity: 0;
transform: translateY(8px);
transition: opacity 0.35s ease-out, transform 0.35s ease-out;
}
.stagger.visible {
opacity: 1;
transform: translateY(0);
}
`;
}
updateStyles();
const generalMenu = document.createElement('div');
generalMenu.id = 'sham-general';
generalMenu.className = 'sham-panel';
document.body.appendChild(generalMenu);
const impMenu = document.createElement('div');
impMenu.id = 'sham-imp';
impMenu.className = 'sham-panel';
document.body.appendChild(impMenu);
const ccMenu = document.createElement('div');
ccMenu.id = 'sham-cc';
ccMenu.className = 'sham-panel';
document.body.appendChild(ccMenu);
function makeDraggable(panel) {
let isDragging = false;
let offset = { x: 0, y: 0 };
const header = panel.querySelector('.sham-header');
header.addEventListener('mousedown', (e) => {
if (e.target.closest('.sham-close')) return;
isDragging = true;
const rect = panel.getBoundingClientRect();
offset.x = e.clientX - rect.left;
offset.y = e.clientY - rect.top;
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
panel.style.left = (e.clientX - offset.x) + 'px';
panel.style.top = (e.clientY - offset.y) + 'px';
}
});
document.addEventListener('mouseup', () => isDragging = false);
}
let impHTML = "";
Object.keys(IMPORTANT_BUFFS).forEach(file => {
impHTML += `<div class="toggle-row stagger">
<div class="toggle-label">
<span class="module-name">${IMPORTANT_BUFFS[file]}</span>
</div>
<label class="toggle-switch">
<input type="checkbox" data-type="imp" data-file="${file}" ${config.activeImportants[file] ? 'checked' : ''}>
<span class="slider"></span>
</label>
</div>`;
});
let ccHTML = "";
Object.keys(MANAGED_BUFFS).forEach(file => {
ccHTML += `<div class="toggle-row stagger">
<div class="toggle-label">
<span class="module-name">${MANAGED_BUFFS[file]}</span>
</div>
<label class="toggle-switch">
<input type="checkbox" data-type="cc" data-file="${file}" ${config.activeCCs[file] ? 'checked' : ''}>
<span class="slider"></span>
</label>
</div>`;
});
function renderPanels() {
generalMenu.innerHTML = `
<div class="sham-header">
<div class="header-left">
<img src="https://hordes.io/data/ui/classes/3.avif">
<h3>SHAM UI</h3>
</div>
<div class="sham-close">×</div>
</div>
<div class="sham-content">
<div class="sham-warning stagger">CC Indicator only works if you use 1 player per party row or else it'll cause bugs.</div>
<div class="toggle-row stagger">
<div class="toggle-label">
<span class="module-name"><strong>Enable UI</strong></span>
</div>
<label class="toggle-switch">
<input type="checkbox" id="master-switch" ${config.isEnabled ? 'checked' : ''}>
<span class="slider"></span>
</label>
</div>
<div class="toggle-row stagger">
<div class="toggle-label">
<span class="module-name"><strong>Mouse-Over</strong></span>
</div>
<label class="toggle-switch">
<input type="checkbox" id="mouse-switch" ${config.mouseOverTarget ? 'checked' : ''}>
<span class="slider"></span>
</label>
</div>
<div class="sham-label stagger"><span>HP Threshold</span> <span id="val-hp">${config.hpThreshold}%</span></div>
<input class="stagger" type="range" id="cfg-hp" min="10" max="100" value="${config.hpThreshold}" oninput="document.getElementById('val-hp').innerText = this.value + '%'">
<div class="sham-label stagger"><span>Pulse Speed</span> <span id="val-speed">${config.pulseSpeed}s</span></div>
<input class="stagger" type="range" id="cfg-speed" min="0.1" max="1.0" step="0.1" value="${config.pulseSpeed}" oninput="document.getElementById('val-speed').innerText = this.value + 's'">
<div class="sham-label stagger"><span>Pulse Intensity</span> <span id="val-strength">${Math.round(config.pulseStrength * 100)}%</span></div>
<input class="stagger" type="range" id="cfg-strength" min="0.1" max="1.0" step="0.05" value="${config.pulseStrength}" oninput="document.getElementById('val-strength').innerText = Math.round(this.value * 100) + '%'">
<div class="sham-label stagger"><span>Border Size</span> <span id="val-border">${config.borderSize}px</span></div>
<input class="stagger" type="range" id="cfg-border" min="0" max="10" step="1" value="${config.borderSize}" oninput="document.getElementById('val-border').innerText = this.value + 'px'">
<button id="cfg-save" class="sham-btn stagger">SAVE SETTINGS</button>
</div>
`;
impMenu.innerHTML = `
<div class="sham-header">
<div class="header-left"><h3>Important Buffs</h3></div>
<div class="sham-close">×</div>
</div>
<div class="sham-content">
<div class="sham-desc stagger">Uncheck to permanently hide these key buffs/passives from your hotbar.</div>
<div class="sham-list">${impHTML}</div>
</div>
`;
ccMenu.innerHTML = `
<div class="sham-header">
<div class="header-left"><h3>Icon Manager</h3></div>
<div class="sham-close">×</div>
</div>
<div class="sham-content">
<div class="sham-desc stagger">Uncheck to permanently hide these managed icons from your hotbar.</div>
<div class="sham-list">${ccHTML}</div>
</div>
`;
document.querySelectorAll('.sham-close').forEach(btn => btn.onclick = hideMenus);
document.querySelectorAll('.toggle-switch input[type="checkbox"]').forEach(input => {
const row = input.closest('.toggle-row');
if (row) {
const update = () => {
row.classList.toggle('enabled', input.checked);
};
update();
input.addEventListener('change', update);
}
});
document.getElementById('cfg-save').onclick = () => {
config.isEnabled = document.getElementById('master-switch').checked;
config.mouseOverTarget = document.getElementById('mouse-switch').checked;
config.hpThreshold = parseInt(document.getElementById('cfg-hp').value);
config.pulseSpeed = parseFloat(document.getElementById('cfg-speed').value);
config.pulseStrength = parseFloat(document.getElementById('cfg-strength').value);
config.borderSize = parseInt(document.getElementById('cfg-border').value);
document.querySelectorAll('#sham-imp input[data-type="imp"]').forEach(cb => {
config.activeImportants[cb.dataset.file] = cb.checked;
});
document.querySelectorAll('#sham-cc input[data-type="cc"]').forEach(cb => {
config.activeCCs[cb.dataset.file] = cb.checked;
});
localStorage.setItem('shamConfig', JSON.stringify(config));
updateStyles();
hideMenus();
};
}
renderPanels();
makeDraggable(generalMenu);
makeDraggable(impMenu);
makeDraggable(ccMenu);
function showMenus() {
const panelWidth = 310;
const gap = 15;
const totalWidth = panelWidth * 3 + gap * 2;
let startLeft = Math.max(10, (window.innerWidth - totalWidth) / 2);
generalMenu.style.left = startLeft + 'px';
impMenu.style.left = (startLeft + panelWidth + gap) + 'px';
ccMenu.style.left = (startLeft + panelWidth * 2 + gap * 2) + 'px';
const topPos = Math.max(20, (window.innerHeight - 500) / 2);
[generalMenu, impMenu, ccMenu].forEach(menu => {
menu.style.top = topPos + 'px';
menu.style.display = 'flex';
void menu.offsetWidth;
menu.classList.add('active');
});
[generalMenu, impMenu, ccMenu].forEach(menu => {
const items = menu.querySelectorAll('.stagger');
items.forEach((item, index) => {
setTimeout(() => item.classList.add('visible'), 120 + index * 45);
});
});
}
function hideMenus() {
[generalMenu, impMenu, ccMenu].forEach(menu => {
menu.querySelectorAll('.stagger').forEach(item => item.classList.remove('visible'));
menu.classList.remove('active');
});
setTimeout(() => {
[generalMenu, impMenu, ccMenu].forEach(menu => menu.style.display = 'none');
}, 300);
}
window.addEventListener('keydown', (e) => {
if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) return;
if (e.shiftKey && e.key.toLowerCase() === 'n') {
e.preventDefault();
if (generalMenu.style.display === 'flex') hideMenus();
else showMenus();
} else if (e.key === 'Escape' && generalMenu.style.display === 'flex') {
e.preventDefault();
hideMenus();
}
});
document.addEventListener('mouseover', (e) => {
if (config.isEnabled && config.mouseOverTarget) {
const t = e.target.closest('div[class*="targetable"]');
if (t) t.click();
}
});
let lastCCState = false;
// Main pulse logic (kept exactly as in your working script, including spatial CC matching)
setInterval(() => {
if (!config.isEnabled) return;
document.querySelectorAll('.hp-panic-active, .cc-active-pulse').forEach(el => {
el.classList.remove('hp-panic-active', 'cc-active-pulse');
});
const allBars = Array.from(document.querySelectorAll('div[class*="panel-black"][class*="barsInner"]'));
const ccIcons = Array.from(document.getElementsByTagName('img')).filter(img => AUTO_CC_ICONS.some(cc => img.src.includes(cc)));
let currentCCDetected = false;
ccIcons.forEach(icon => {
const iconRect = icon.getBoundingClientRect();
if (iconRect.width === 0) return;
const iconCenter = { x: iconRect.left + iconRect.width / 2, y: iconRect.top + iconRect.height / 2 };
let closestBar = null;
let minDistance = Infinity;
allBars.forEach(bar => {
const barRect = bar.getBoundingClientRect();
const dist = Math.hypot(iconCenter.x - (barRect.left + barRect.width / 2), iconCenter.y - (barRect.top + barRect.height / 2));
if (dist < minDistance) {
minDistance = dist;
closestBar = bar;
}
});
if (closestBar && minDistance < 250) {
closestBar.classList.add('cc-active-pulse');
currentCCDetected = true;
}
});
allBars.forEach(bar => {
if (bar.classList.contains('cc-active-pulse')) return;
const bgHealth = bar.querySelector('div[class*="bghealth"]');
if (bgHealth && bgHealth.style.width) {
const hpWidth = parseFloat(bgHealth.style.width);
if (hpWidth <= config.hpThreshold && hpWidth > 0) {
bar.classList.add('hp-panic-active');
}
}
});
if (!currentCCDetected) lastCCState = false;
}, 150);
// Separate fast icon deletion (as in your script for performance)
setInterval(() => {
if (!config.isEnabled) return;
const allImgs = document.getElementsByTagName('img');
for (let i = allImgs.length - 1; i >= 0; i--) {
const img = allImgs[i];
const srcRaw = img.src.split('/').pop() || "";
const src = srcRaw.split('?')[0];
const shouldHideCC = MANAGED_BUFFS[src] && config.activeCCs[src] === false;
const shouldHideImp = IMPORTANT_BUFFS[src] && config.activeImportants[src] === false;
if (shouldHideCC || shouldHideImp) {
const slot = img.closest('.slot');
if (slot) slot.remove();
else img.remove();
}
}
}, 50);
})();