// ==UserScript==
// @name Elethor General Purpose
// @description Provides some general additions to Elethor
// @namespace https://www.elethor.com/
// @version 1.3.2
// @author Anders Morgan Larsen (Xortrox)
// @match https://elethor.com/*
// @match https://www.elethor.com/*
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
const currentUserData = {};
const moduleName = 'Elethor General Purpose';
const version = '1.3.2'
const playerHealthBarElementDefinition = '.progress.is-medium.is-danger';
const playerHealthBarTextElementDefinition = '.is-fight-health';
function getBattleHealthPercentage() {
const playerHealth = document.querySelector(playerHealthBarElementDefinition);
if (!playerHealth) {
return 0;
}
return playerHealth.value / playerHealth.max * 100;
}
function highlightKills() {
document.querySelectorAll('.has-combat-log>li').forEach((e) => {
if (!e || !e.setAttribute || !e.innerText) {
return;
}
if (e.innerText.includes('You killed')) {
e.setAttribute('style', 'color:lime');
} else if (e.innerText.includes('The monster did')){
e.setAttribute('style', 'color:red');
} else {
e.setAttribute('style', '');
}
});
}
function initializeUpdateBattleHealthPercentage() {
if (window.elethorExtrasInterval) {
clearInterval(window.elethorExtrasInterval);
}
window.elethorExtrasInterval = setInterval(() => {
const playerHealthText = document.querySelector(playerHealthBarTextElementDefinition);
const healthPercentage = getBattleHealthPercentage();
if (playerHealthText && healthPercentage && !isNaN(healthPercentage)) {
let percentageDisplay;
if (playerHealthText.children.length === 0) {
percentageDisplay = document.createElement('span');
playerHealthText.appendChild(percentageDisplay);
} else {
percentageDisplay = playerHealthText.children[0];
}
if (percentageDisplay) {
percentageDisplay.innerText = ` (${healthPercentage.toFixed(3)}%)`;
}
}
highlightKills();
}, 500);
console.log(`[${moduleName} v${version}] Battle Percentage initialized.`);
}
function initializeToastKiller() {
document.addEventListener('click', function(e) {
if (e.target
&& e.target.className
&& e.target.className.includes
&& e.target.className.includes('toasted')
) {
e.target.remove();
}
});
console.log(`[${moduleName} v${version}] Toast Killer initialized.`);
}
function initializeXHRHook() {
let rawSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
if (!this._hooked) {
this._hooked = true;
this.addEventListener('readystatechange', function() {
if (this.readyState === XMLHttpRequest.DONE) {
setupHook(this);
}
}, false);
}
rawSend.apply(this, arguments);
}
function setupHook(xhr) {
if (window.elethorGeneralPurposeOnXHR) {
const e = new Event('EGPXHR');
e.xhr = xhr;
window.elethorGeneralPurposeOnXHR.dispatchEvent(e);
}
}
window.elethorGeneralPurposeOnXHR = new EventTarget();
console.log(`[${moduleName} v${version}] XHR Hook initialized.`);
}
function initializeUserLoadListener() {
elethorGeneralPurposeOnXHR.addEventListener('EGPXHR', function (e) {
if (e && e.xhr
&& e.xhr.responseURL
&& e.xhr.responseURL.endsWith
&& e.xhr.responseURL.endsWith('/game/user')
) {
try {
const userData = JSON.parse(e.xhr.responseText);
console.log(`[${moduleName} v${version}] User Data hook:`, userData);
if (userData) {
for (const key of Object.keys(userData)) {
currentUserData[key] = userData[key];
}
console.log(`[${moduleName} v${version}] User Data loaded:`, currentUserData);
}
} catch (e) {
console.log(`[${moduleName} v${version}] Error parsing userData:`, e);
}
}
});
console.log(`[${moduleName} v${version}] User Load Listener initialized.`);
}
function initializeInventoryStatsLoadListener() {
elethorGeneralPurposeOnXHR.addEventListener('EGPXHR', function (e) {
if (e && e.xhr
&& e.xhr.responseURL
&& e.xhr.responseURL.endsWith
&& e.xhr.responseURL.endsWith('/game/inventory/stats')
) {
setTimeout(() => {
updateEquipmentPercentageSummary();
setTimeout(updateInventoryStatsPercentages, 1000);
});
}
});
console.log(`[${moduleName} v${version}] Inventory Stats Load Listener initialized.`);
}
function updateEquipmentPercentageSummary() {
document.querySelector('.is-equipment>div>div').setAttribute('style', 'width: 50%')
let percentagesTable = document.querySelector('#egpPercentagesSummary')
if (!percentagesTable){
percentagesTable = document.querySelector('.is-equipment>div>div:nth-child(2)').cloneNode(true);
percentagesTable.setAttribute('style', 'width: 25%');
percentagesTable.id='egpPercentagesSummary';
document.querySelector('.is-equipment>div').appendChild(percentagesTable);
for (const child of percentagesTable.children[0].children) {
if (child && child.children && child.children[0]) {
child.children[0].remove();
}
}
document.querySelector('#egpPercentagesSummary>table>tr:nth-child(8)').setAttribute('style', 'height:43px');
}
}
function getStatSummary(equipment) {
const summary = {
base: {},
energizements: {}
};
if (equipment) {
for (const key of Object.keys(equipment)) {
const item = equipment[key];
/**
* Sums base attributes by name
* */
if (item && item.attributes) {
for (const attributeName of Object.keys(item.attributes)) {
const attributeValue = item.attributes[attributeName];
if (!summary.base[attributeName]) {
summary.base[attributeName] = 0;
}
summary.base[attributeName] += attributeValue;
}
}
/**
* Sums energizements by stat name
* */
if (item && item.upgrade && item.upgrade.energizements) {
for (const energizement of item.upgrade.energizements) {
if (!summary.energizements[energizement.stat]) {
summary.energizements[energizement.stat] = 0;
}
summary.energizements[energizement.stat] += Number(energizement.boost);
}
}
}
}
return summary;
}
function updateInventoryStatsPercentages() {
let percentagesTable = document.querySelector('#egpPercentagesSummary')
if (percentagesTable && currentUserData && currentUserData.equipment){
const statSummary = getStatSummary(currentUserData.equipment);
const baseKeys = Object.keys(statSummary.base);
const energizementKeys = Object.keys(statSummary.energizements);
let allKeys = baseKeys.concat(energizementKeys);
const filterUniques = {};
for (const key of allKeys){
filterUniques[key] = true;
}
allKeys = Object.keys(filterUniques);
allKeys.sort();
allKeys.push('actions');
const tableRows = percentagesTable.children[0].children;
for(const row of tableRows) {
if (row
&& row.children
&& row.children[0]
&& row.children[0].children[0]
) {
const rowText = row.children[0].children[0];
rowText.innerText = '';
}
}
let rowIndex = 0;
for (const key of allKeys) {
if (key === 'puncture') {
continue;
}
const row = tableRows[rowIndex];
if (row
&& row.children
&& row.children[0]
&& row.children[0].children[0]
) {
const rowText = row.children[0].children[0];
const rowBase = statSummary.base[key] || 0;
const rowEnergizement = (statSummary.energizements[key] || 0);
const rowEnergizementPercentage = (statSummary.energizements[key] || 0) * 100;
if (key.startsWith('+')) {
rowText.innerText = `${key} per 10 levels: ${rowEnergizement}`;
} else if (key === 'actions') {
const actions = currentUserData.user.bonus_actions || 0;
rowText.innerText = `Bonus Actions: ${actions}`;
} else {
rowText.innerText = `${key}: ${rowBase} (${rowEnergizementPercentage.toFixed(0)}%)`;
}
rowIndex++;
}
}
}
}
(function run() {
initializeUpdateBattleHealthPercentage();
initializeToastKiller();
initializeXHRHook();
initializeUserLoadListener();
initializeInventoryStatsLoadListener();
console.log(`[${moduleName} v${version}] Loaded.`);
})();
(async function loadRerollDisableButtonModule() {
async function waitForEcho() {
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (window.Echo) {
clearInterval(interval);
resolve();
}
}, 100);
});
}
async function waitForUser() {
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (currentUserData.user && currentUserData.user.id !== undefined) {
clearInterval(interval);
resolve();
}
}, 100);
});
}
await waitForEcho();
await waitForUser();
elethorGeneralPurposeOnXHR.addEventListener('EGPXHR', async function (e) {
if (e && e.xhr && e.xhr.responseURL) {
if(e.xhr.responseURL.includes('/game/energize')) {
const itemID = e.xhr.responseURL.substr(e.xhr.responseURL.lastIndexOf('/')+1);
window.lastEnergizeID = Number(itemID);
}
}
});
function initializeDisableEnergizementButtonOnReroll() {
const privateRoom = `App.User.${currentUserData.user.id}`;
console.log(`[${moduleName} v${version}] Binding to private room ${privateRoom}`);
window.Echo.private(privateRoom).listen(".App\\Domain\\Inventory\\Events\\UpdateItem", (data) => {
if (data && data.item.id === window.lastEnergizeID){
//enableEnergizementButton();
}
});
document.addEventListener('click', function(e) {
try {
if (!e
|| !e.target
|| !e.target.parentElement
|| !e.target.className
|| !e.target.parentElement.className
|| !e.target.innerText
|| !e.target.innerText.includes
) {
return;
}
if ((e.target.innerText.includes('Reroll Energizements') || e.target.innerText.includes('Costs 1 Standard Energizing Shard'))
&& (e.target.className.includes('button') || e.target.parentElement.className.includes('button'))
) {
// disableEnergizementButton();
// setTimeout(enableEnergizementButton, 500);
}
} catch (e) {
console.warn(`[${moduleName} v${version}] Error during document click:`, e);
}
});
}
initializeDisableEnergizementButtonOnReroll();
function disableEnergizementButton() {
const button = getEnergizementButton();
if (!button) {
return;
}
button.setAttribute('style', 'pointer-events: none');
button.setAttribute('disabled', 'true');
}
function enableEnergizementButton() {
const button = getEnergizementButton();
if (!button) {
return;
}
button.removeAttribute('style');
button.removeAttribute('disabled');
}
function getEnergizementButton() {
let button;
document.querySelectorAll('.buttons>.button.is-multiline.is-info>span:first-child').forEach((e) => {
if (e && e.innerText && e.innerText === 'Reroll Energizements') {
button = e.parentElement;
}
});
return button;
}
})();
(function loadResourceNodeUpdater() {
function updateExperienceRates() {
document.querySelectorAll('.is-resource-node').forEach(visualizeResourceNodeExperienceRates);
function visualizeResourceNodeExperienceRates(node) {
const purity = getNodePurityPercentage(node);
const density = getNodeDensityPercentage(node);
const experience = getNodeExperience(node);
const ore = 16;
const experienceRate = getExperienceRate(density, experience);
const oreRate = getOreRate(density, purity, ore);
node.children[0].setAttribute('data-after', `${experienceRate} xp/h ${oreRate} ore/h`);
}
function getNodePurityPercentage(node) {
const column = node.children[0].children[2];
const label = column.getElementsByClassName('has-text-weight-bold')[0].parentElement;
const percentage = Number(label.innerText.replace('%','').split(':')[1])
return percentage;
}
function getNodeDensityPercentage(node) {
const column = node.children[0].children[1];
const label = column.getElementsByClassName('has-text-weight-bold')[0].parentElement;
const percentage = Number(label.innerText.replace('%','').split(':')[1])
return percentage;
}
function getNodeExperience(node) {
const column = node.children[0].children[3];
const label = column.getElementsByClassName('has-text-weight-bold')[0].parentElement;
const value = Number(label.innerText.replace('%','').split(':')[1])
return value;
}
function getExperienceRate(density, experience) {
return Number((3600 / (60 / (density / 100)) * experience).toFixed(3));
}
function getOreRate(density, purity, ore) {
return Number((3600 / (60 / (density / 100)) * (purity / 100) * ore).toFixed(3));
}
}
updateExperienceRates();
window.elethorResourceInterval = setInterval(updateExperienceRates, 500);
initializeResourceNodeView();
async function initializeResourceNodeView() {
await waitForField(document, 'head');
var css = '.columns.is-mobile.is-size-7-mobile::after { content: attr(data-after); padding: 12px;}',
head = document.head || document.getElementsByTagName('head')[0],
style = document.createElement('style');
head.appendChild(style);
style.type = 'text/css';
if (style.styleSheet){
// This is required for IE8 and below.
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
async function waitForField(target, field) {
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (target[field] !== undefined) {
clearInterval(interval);
resolve();
}
}, 100);
});
}
})();
})();