// ==UserScript==
// @name DHO Fixed
// @namespace FileFace
// @description Improve Diamond Hunt Online and fix some inconsistencies
// @version 0.54.6
// @author Zorbing
// @grant none
// @run-at document-start
// @include http://www.diamondhunt.co/game.php
// ==/UserScript==
/**
* TODO:
* - restyle tabs (allow sub-tabs)
* - add activity log (intercepting the dialog and log some messages)
* - add new item style for mining engineer
* - hide browser notifications if the corresponding tab is opened
*/
(function ()
{
'use strict';
const settings = {
reorderFarming: {
title: 'Set seed orders coherent'
, defaultValue: true
}
, applyNewItemStyle: {
title: 'Apply a new item style'
, defaultValue: true
}
, applyNewKeyItemStyle: {
title: 'Apply a new key item and machinery style'
, defaultValue: true
}
, improveDialogBtns: {
title: 'Improve button captions in dialogs'
, defaultValue: true
}
, improveMachineryDialog: {
title: 'Improve the machinery dialog'
, defaultValue: true
}
, hideSomeCraftRecipes: {
title: 'Hide some crafting recipes'
, defaultValue: true
}
, hideMaxRecipes: {
title: 'Hide recipes of maxed machines'
, defaultValue: true
}
, expandEquipment: {
title: 'Expand crafting recipes of equipment'
, defaultValue: true
}
, hideEquipment: {
title: 'Hide inferiour equipment (only up to gold)'
, defaultValue: true
}
, hideUnnecessaryPrice: {
title: 'Hide unnecessary prices'
, defaultValue: true
}
, useFastLevelCalculation: {
title: 'Use fast level calculation'
, defaultValue: true
}
, showNotifications: {
title: 'Show notifications for events'
, defaultValue: true
}
, useNewChat: {
title: 'Use the new chat with local history'
, defaultValue: true
}
};
let fullyLoaded = false;
let notify = () => Promise.reject('Notifications disabled');
/**
* global constants
*/
const maxLevel = 100;
const maxLevelVirtual = 1000;
const furnaceLevels = ['', 'stone', 'bronze', 'iron', 'silver', 'gold', 'ancient', 'promethium', 'runite', 'dragon'];
const ovenLevels = ['bronze', 'iron', 'silver', 'gold', 'ancient', 'promethium', 'runite', 'dragon'];
const barTypes = ['bronze', 'iron', 'silver', 'gold', 'promethium', 'runite'];
const oilConsumption = {
'drill': 1
, 'crusher': 15
, 'giantDrill': 30
, 'roadHeader': 50
, 'bucketWheelExcavator': 150
, 'giantBWE': 500
, 'sandCollector': 5
};
const machineNames = {
'drill': 'Mining Drill'
, 'crusher': 'Crusher'
, 'giantDrill': 'Giant Drill'
, 'roadHeader': 'Road Header'
, 'bucketWheelExcavator': 'Excavator'
, 'giantBWE': 'Mega Excavator'
, 'sandCollector': 'Sand Collector'
};
/**
* observer stuff
*/
let observedKeys = new Map();
/**
* Observes the given key for change
*
* @param {string} key The name of the variable
* @param {Function} fn The function which is called on change
*/
function observe(key, fn)
{
if (key instanceof Array)
{
for (let k of key)
{
observe(k, fn);
}
}
else
{
if (!observedKeys.has(key))
{
observedKeys.set(key, new Set());
}
observedKeys.get(key).add(fn);
}
return fn;
}
function unobserve(key, fn)
{
if (key instanceof Array)
{
let ret = [];
for (let k of key)
{
ret.push(unobserve(k, fn));
}
return ret;
}
if (!observedKeys.has(key))
{
return false;
}
return observedKeys.get(key).delete(fn);
}
function initObservable()
{
const oldLoadGlobals = window.loadGlobals;
window.loadGlobals = (key, newValue) =>
{
if (key === undefined)
{
return;
}
const oldValue = window[key];
const ret = oldLoadGlobals(key, newValue);
if (oldValue !== newValue)
{
(observedKeys.get(key) || []).forEach(fn => fn(key, oldValue, newValue));
}
return ret;
};
}
/**
* settings
*/
function getSetting(key)
{
if (!settings.hasOwnProperty(key))
{
return;
}
const name = 'setting.' + key;
return localStorage.hasOwnProperty(name) ? JSON.parse(localStorage.getItem(name)) : settings[key].defaultValue;
}
function setSetting(key, value)
{
if (!settings.hasOwnProperty(key))
{
return;
}
localStorage.setItem('setting.' + key, JSON.stringify(value));
}
function initSettings()
{
if (!localStorage.hasOwnProperty('setSoundToggleDefault'))
{
const defaultSound = 'off';
localStorage.setItem('soundToggle', defaultSound);
localStorage.setItem('setSoundToggleDefault', true);
document.getElementById('sound-toggle').innerHTML = defaultSound;
}
const table = document.getElementById('settings-tab').querySelector('table');
if (!table)
{
return;
}
const headerRow = table.insertRow(-1);
headerRow.innerHTML = `<th style="background-color:black;color:orange">
Userscript "DHO Fixed"<br>
<span style="font-size: 0.9rem;">(changes require reloading the tab)</span>
</th>`;
for (let key in settings)
{
let value = getSetting(key);
const row = table.insertRow(-1);
row.innerHTML = `<td id="fake-link-top">
${settings[key].title}: <span style="color:cyan;" id="userscript-${key}">${value ? 'on' : 'off'}</span>
</td>`;
const indicator = document.getElementById('userscript-' + key);;
row.addEventListener('click', () =>
{
value = !value;
setSetting(key, value);
indicator.textContent = value ? 'on' : 'off';
});
}
const settingLink = document.querySelector('.top-menu td[onclick^="openTab"]');
settingLink.addEventListener('click', function ()
{
const activeTab = document.querySelector('#tab-tr td[style^="background: linear-gradient(rgb"]');
if (activeTab)
{
activeTab.style.background = 'linear-gradient(black, grey)';
}
});
}
/**
* fix key items
*/
function fixKeyItems()
{
// remove unnecessary br element
const oilPump = document.getElementById('key-item-handheldOilPump-box');
let br = oilPump && oilPump.nextElementSibling;
if (!br)
{
br = document.createElement('br');
}
// add br element after img in oil pipe element
const oilPipe = document.getElementById('key-item-bindedOilPipe-box');
let img = oilPipe && oilPipe.children[0];
img = img && img.children[0];
img.parentNode.insertBefore(br, img.nextSibling);
}
/**
* fix farming
*/
const seedOrder = ['bloodLeafSeeds', 'redMushroomSeeds', 'dottedGreenLeafSeeds', 'potatoSeeds', 'strawberrySeeds', 'greenLeafSeeds', 'redMushroomTreeSeeds', 'wheatSeeds', 'blewitMushroomSeeds', 'limeLeafSeeds', 'blewitMushroomTreeSeeds', 'snapeGrassSeeds', 'starDustSeeds', 'appleTreeSeeds', 'iceBerrySeeds', 'goldLeafSeeds', 'starDustTreeSeeds', 'stripedLeafSeeds', 'essenceSeeds', 'crystalLeafSeeds', 'megaDottedGreenLeafSeeds', 'megaRedMushroomSeeds', 'essenceTreeSeeds', 'megaGreenLeafSeeds', 'stripedCrystalLeafSeeds', 'megaBlewitMushroomSeeds', 'megaLimeLeafSeeds'];
const seeds = {
bloodLeafSeeds: {
title: 'Blood Leaf Seed'
, level: 1
, diesUntil: 0
, time: 5
, xp: 1e6
}
, redMushroomSeeds: {
title: 'Red Mushroom Seed'
, level: 1
, diesUntil: 0
, time: 15
, xp: 100
}
, dottedGreenLeafSeeds: {
title: 'Green Dotted Leaf Seed'
, level: 1
, diesUntil: 15
, time: 30
, xp: 250
}
, potatoSeeds: {
title: 'Potato'
, level: 5
, diesUntil: 0
, time: 15
, xp: 35
}
, strawberrySeeds: {
title: 'Strawberry Seed'
, level: 10
, diesUntil: 0
, time: 30
, xp: 85
}
, greenLeafSeeds: {
title: 'Green Leaf Seed'
, level: 10
, diesUntil: 25
, time: 60
, xp: 500
}
, redMushroomTreeSeeds: {
title: 'Red Mushroom Tree Seed'
, level: 10
, diesUntil: 30
, time: 8*60
, xp: 2e3
}
, wheatSeeds: {
title: 'Wheat Seed'
, level: 15
, diesUntil: 0
, time: 15
, xp: 95
}
, blewitMushroomSeeds: {
title: 'Blewit Mushroom Seed'
, level: 15
, diesUntil: 0
, time: 20
, xp: 200
}
, limeLeafSeeds: {
title: 'Lime Leaf Seed'
, level: 20
, diesUntil: 40
, time: 1.5*60
, xp: 1500
}
, blewitMushroomTreeSeeds: {
title: 'Blewit Mushroom Tree Seed'
, level: 20
, diesUntil: 40
, time: 10*60
, xp: 4e3
}
, snapeGrassSeeds: {
title: 'Snape Grass Seed'
, level: 25
, diesUntil: 0
, time: 30
, xp: 300
}
, starDustSeeds: {
title: 'Stardust Seed'
, level: 30
, diesUntil: 0
, time: 30
, xp: 750
}
, appleTreeSeeds: {
title: 'Apple Tree Seed'
, level: 30
, diesUntil: 45
, time: 8*60
, xp: 5e3
}
, iceBerrySeeds: {
title: 'Ice Berry Seed'
, level: 35
, diesUntil: 0
, time: 60
, xp: 450
}
, goldLeafSeeds: {
title: 'Gold Leaf Seed'
, level: 40
, diesUntil: 55
, time: 4*60
, xp: 10e3
}
, starDustTreeSeeds: {
title: 'Stardust Tree Seed'
, level: 40
, diesUntil: 55
, time: 5*60
, xp: 15e3
}
, stripedLeafSeeds: {
title: 'Striped Gold Leaf Seed'
, level: 55
, diesUntil: 70
, time: 7*60
, xp: 25e3
}
, essenceSeeds: {
title: 'Essence Seed'
, level: 60
, diesUntil: 0
, time: 3*60
, xp: 30e3
}
, crystalLeafSeeds: {
title: 'Crystal Leaf Seed'
, level: 70
, diesUntil: 85
, time: 10*60
, xp: 40e3
}
, megaDottedGreenLeafSeeds: {
title: 'Mega Dotted Green Leaf Seed'
, level: 70
, diesUntil: 0
, time: 16*60
, xp: 12500
}
, megaRedMushroomSeeds: {
title: 'Mega Red Mushroom Seed'
, level: 70
, diesUntil: 0
, time: 16*60
, xp: 20500
}
, essenceTreeSeeds: {
title: 'Essence Tree Seed'
, level: 80
, diesUntil: 90
, time: 12*60
, xp: 50500
}
, megaGreenLeafSeeds: {
title: 'Mega Green Leaf Seed'
, level: 80
, diesUntil: 0
, time: 20*60
, xp: 21e3
}
, stripedCrystalLeafSeeds: {
title: 'Striped Crystal Leaf Seed'
, level: 85
, diesUntil: 95
, time: 15*60
, xp: 90e3
}
, megaBlewitMushroomSeeds: {
title: 'Mega Blewit Mushroom Seed'
, level: 85
, diesUntil: 0
, time: 20*60
, xp: 21500
}
, megaLimeLeafSeeds: {
title: 'Mega Lime Leaf Seed'
, level: 85
, diesUntil: 0
, time: 23*60
, xp: 32e3
}
};
function fixFarming()
{
const inputs = document.querySelectorAll('#dialog-planter input[type="image"]');
for (let i = inputs.length-1; i >= 0; i--)
{
const input = inputs[i];
const key = input.id.replace('planter-input-img-', '');
const seed = seeds[key];
input.title = seed.title;
}
if (!getSetting('reorderFarming'))
{
return;
}
let planterEl = inputs[0];
const planterParent = planterEl.parentNode;
let boxEl = document.querySelector('#farming-tab .inventory-item-box-farming').parentNode;
const boxParent = boxEl.parentNode;
const btnParent = document.getElementById('seed-menu-popup');
let btnEl = btnParent.firstElementChild;
for (let i = seedOrder.length-1; i >= 0; i--)
{
const key = seedOrder[i];
const input = document.getElementById('planter-input-img-' + key);
if (input)
{
planterParent.insertBefore(input, planterEl);
planterParent.insertBefore(document.createTextNode(' '), planterEl);
planterEl = input;
}
const box = document.getElementById('item-' + key + '-box');
if (box)
{
boxParent.insertBefore(box.parentNode, boxEl);
boxParent.insertBefore(document.createTextNode(' '), boxEl);
boxEl = box.parentNode;
}
const btn = document.getElementById('btn-' + key);
if (btn)
{
btn.title = btn.title.replace('dieing', 'dying');
btnParent.insertBefore(btn, btnEl);
btnParent.insertBefore(document.createTextNode(' '), btnEl);
btnEl = btn;
}
}
}
/**
* fix server message
*/
function fixServerMsg()
{
const serverMsgEl = document.querySelector('#server-inner-msg');
if (!serverMsgEl)
{
return;
}
const serverMsg = serverMsgEl.textContent;
const close = document.querySelector('#server-top-msg > *:last-child');
if (localStorage.getItem('closedServerMsg') == serverMsg)
{
close.click();
return;
}
close.addEventListener('click', function ()
{
localStorage.setItem('closedServerMsg', serverMsg);
});
}
/**
* highlight requirements
*/
const highlightBgColor = 'hsla(0, 100%, 90%, 1)';
const imgSrc2Key = {
'bronzebar': 'bronzeBar'
, 'ironbar': 'ironBar'
, 'silverbar': 'silverBar'
, 'goldbar': 'goldBar'
, 'stonefurnace': 'stoneFurnace'
, 'bronzefurnace': 'bronzeFurnace'
, 'ironfurnace': 'ironFurnace'
, 'silverfurnace': 'silverFurnace'
, 'goldfurnace': 'goldFurnace'
, 'pic_coin': 'coins'
, 'stardust': 'starDust'
, 'treasureKey': 'treasureChestKey'
, 'dottedgreenleaf': 'dottedGreenLeaf'
, 'redmushroom': 'redMushroom'
, 'greenleaf': 'greenLeaf'
, 'limeleaf': 'limeLeaf'
, 'blewitmushroom': 'blewitMushroom'
, 'goldleaf': 'goldLeaf'
, 'pureWater': 'pureWaterPotion'
, 'snapegrass': 'snapeGrass'
, 'crystalleaf': 'crystalLeaf'
, 'starDustConverter': 'starGemPotion'
, 'superStargemPotion': 'superStarGemPotion'
, 'superoilpotion': 'superOilPotion'
, 'wooden_slave': 'miners'
, 'fishingRodFarmer': 'fishingRod'
, 'goldenStriper': 'goldenStriperPotion'
, 'orb': 'orbOfTransformation'
, 'anyorb': 'emptyBlueOrb'
, 'anyorb2': 'emptyGreenOrb'
, 'upgradedOrb': 'superOrbOfTransformation'
};
const imgSrc2LevelKey = {
'watering-can': 'merchanting'
, 'cookingskill': 'cooking'
, 'archaeology': 'exploring'
, 'wizardHatIcon': 'magic'
};
function amount2Int(str)
{
return parseInt(str.replace(/M/i, '000000').replace(/B/i, '000000000').replace(/\D/g, ''), 10);
}
function checkRequirements(row, xpKey, init = true)
{
const isRed = row.style.backgroundColor == 'rgb(255, 128, 128)';
let everythingFulfilled = true;
let keys2Observe = [];
const levelEl = row.cells[2];
const neededLevel = parseInt(levelEl.textContent, 10);
const levelHighEnough = neededLevel <= window.getLevel(window[xpKey]);
levelEl.style.color = levelHighEnough ? '' : 'red';
everythingFulfilled = everythingFulfilled && levelHighEnough;
keys2Observe.push(xpKey);
const reqEl = row.cells[3];
const children = reqEl.children;
// check for each requirement if it is fulfilled
for (let i = 0; i < children.length; i++)
{
const el = children[i];
if (el.tagName != 'IMG')
{
continue;
}
const imgKey = el.src.replace(/^.+images\/.*?([^\/]+)\..+$/, '$1');
const key = imgSrc2Key[imgKey] || imgKey;
// wrap the amount with a span element
let valueSpan = el.nextSibling;
if (valueSpan.nodeType == Node.TEXT_NODE)
{
const valueTextNode = valueSpan;
valueSpan = document.createElement('span');
valueTextNode.parentNode.insertBefore(valueSpan, valueTextNode);
valueSpan.appendChild(valueTextNode);
const text = valueTextNode.textContent;
if (/^\s?\d[\d',\.]*$/.test(text))
{
valueTextNode.textContent = ' ' + formatNumber(text.replace(/\D/g, ''));
}
}
const amount = amount2Int(valueSpan.textContent);
const has = parseInt(window[key] || '0', 10);
const isSkill = imgSrc2LevelKey.hasOwnProperty(key);
let fulfilled = has >= amount;
if (isSkill)
{
const xpKey = imgSrc2LevelKey[key] + 'Xp';
fulfilled = window.getLevel(window[xpKey]) >= amount;
keys2Observe.push(xpKey);
}
else if (key == 'gem')
{
fulfilled = window.sapphire >= amount || window.emerald >= amount || window.ruby >= amount || window.diamond >= amount;
keys2Observe.push('sapphire', 'emerald', 'ruby', 'diamond');
}
else if (/furnace/i.test(key))
{
const furnaceLevel = furnaceLevels.indexOf(key.replace(/furnace/i, ''));
fulfilled = fulfilled || parseInt(window.bindedFurnaceLevel, 10) >= furnaceLevel;
keys2Observe.push(key, 'bindedFurnaceLevel');
}
else if (key == 'anybar')
{
const amountArray = valueSpan.parentNode.getAttribute('tooltip').replace(/\D*$/, '').split('/')
.map(str => amount2Int(str));
fulfilled = false;
for (let i = 0; i < barTypes.length; i++)
{
const bar = barTypes[i];
fulfilled = fulfilled || window[bar + 'Bar'] >= amountArray[i];
keys2Observe.push(bar);
}
}
else if (/(?:wand|staff)$/i.test(key))
{
const bindedKey = 'binded' + key[0].toUpperCase() + key.substr(1);
fulfilled = fulfilled || window[bindedKey] > 0;
keys2Observe.push(key, bindedKey);
}
else
{
if (!window.hasOwnProperty(imgKey) && !imgSrc2Key.hasOwnProperty(imgKey))
{
console.debug('missing key handling:', key, el);
}
keys2Observe.push(key);
}
valueSpan.style.color = fulfilled ? '' : 'red';
everythingFulfilled = everythingFulfilled && (isSkill || fulfilled);
}
levelEl.style.backgroundColor = everythingFulfilled ? '' : highlightBgColor;
reqEl.style.backgroundColor = everythingFulfilled ? '' : highlightBgColor;
row.style.backgroundColor = everythingFulfilled ? 'rgb(194, 255, 133)' : 'rgb(255, 128, 128)';
if (init)
{
observe(keys2Observe, () => checkRequirements(row, xpKey, false));
}
}
function highlightRequirements()
{
const craftingTables = {
'crafting': {
tabId: 'crafting'
, xp: 'crafting'
}
, 'brewing': {
tabId: 'brewing'
, xp: 'brewing'
}
, 'achCraft': {
tabId: 'archaeology-crafting'
, xp: 'crafting'
}
, 'cooking': {
tabId: 'cooking'
, xp: 'cooking'
}
, 'magicCraft': {
tabId: 'magiccrafting'
, xp: 'crafting'
}
, 'spellbook': {
tabId: 'spellbook'
, xp: 'magic'
}
};
for (let key in craftingTables)
{
const info = craftingTables[key];
const xpName = info.xp + 'Xp';
const table = document.querySelector('#' + info.tabId + '-tab table.table-stats');
const rows = table.rows;
for (let i = 0; i < rows.length; i++)
{
const row = rows[i];
if (row.getElementsByTagName('th').length > 0 || row.id == 'craft-ghostKey')
{
continue;
}
checkRequirements(row, xpName, true);
}
}
// hightlight mining level for mining table
function highlightMiningLevel()
{
const miningLevel = window.getLevel(window.miningXp);
const table = document.querySelector('#mining-tab table.table-stats');
const rows = table.rows;
for (let i = 2; i < rows.length; i++)
{
const row = rows[i];
const level = parseInt(row.cells[2].textContent, 10);
const tooLow = level > miningLevel;
row.cells[2].style.color = tooLow ? 'red' : '';
row.style.backgroundColor = tooLow ? 'hsla(0, 100%, 90%, 1)' : '';
}
}
highlightMiningLevel();
observe('miningXp', () => highlightMiningLevel());
const oldLoadGhostPirates = window.loadGhostPirates;
const ghostKeyRow = document.getElementById('craft-ghostKey');
window.loadGhostPirates = () =>
{
oldLoadGhostPirates();
if (ghostEssenceTimer > 0)
{
// this method is called once per second, so there is no need for observing any values
checkRequirements(ghostKeyRow, 'craftingXp', false);
}
};
function highlightFarmingLevel()
{
const farmingLevel = window.getLevel(window.merchantingXp);
const seedBtns = document.querySelectorAll('#seed-menu-popup > div.dialogue-seed-btn');
for (let i = 0; i < seedBtns.length; i++)
{
const seedBtn = seedBtns[i];
const table = seedBtn.firstElementChild;
const levelCell = table.rows[0].cells[1];
const level = parseInt(levelCell.textContent.replace(/\D/g, ''), 10);
const tooLow = level > farmingLevel;
seedBtn.style.backgroundColor = tooLow ? 'hsla(0, 50%, 75%, 1)' : '';
levelCell.style.color = tooLow ? 'red' : '';
levelCell.style.textShadow = tooLow ? '0 0 5px white' : '';
}
}
highlightFarmingLevel();
observe('merchantingXp', () => highlightFarmingLevel());
// achievement upgrades
function highlightAchievementUpgrades()
{
const points = parseInt(window.achPoints, 10);
const spans = document.querySelectorAll('span[id^="cost-ach-"][id$="AchUpgrade"]');
for (let i = 0; i < spans.length; i++)
{
const span = spans[i];
const notEnough = parseInt(span.textContent, 10) > points;
span.style.setProperty('color', notEnough ? 'red' : '', 'important');
span.style.fontWeight = notEnough ? 'bold' : '';
}
}
highlightAchievementUpgrades();
observe('achPoints', () => highlightAchievementUpgrades());
}
/**
* fix market
*/
function fixMarket()
{
// create an observer instance
const observer = new MutationObserver(function(mutations)
{
mutations.forEach(function(mutation)
{
// hide duplicate orb
var orbs = mutation.target.querySelectorAll('input[alt="upgradeEnchantedRake"]');
if (orbs.length > 1)
{
orbs[1].style.display = 'none';
}
});
});
// configuration of the observer:
const config = {
childList: true
};
const table = document.getElementById('selling-tradable-table');
observer.observe(table, config);
// fix loading icons
const loadingImgs = document.querySelectorAll('[src="images/loading_statique.png"]');
for (var i = 0; i < loadingImgs.length; i++)
{
loadingImgs[i].src = 'images/loading.gif';
}
const oldFilterBuyables = window.filterBuyables;
let lastFilterText = null;
window.filterBuyables = (text) =>
{
lastFilterText = text;
return oldFilterBuyables(text);
};
const oldApplyToBuyingTable = window.applyToBuyingTable;
window.applyToBuyingTable = (...args) =>
{
const ret = oldApplyToBuyingTable(...args);
if (lastFilterText != null)
{
window.filterBuyables(lastFilterText);
}
return ret;
};
// add "clear search"-button
const searchInput = document.querySelector('input[onkeyup^="filterBuyables"]');
searchInput.id = 'market-search';
const tmpWrapper = document.createElement('templateWrapper');
tmpWrapper.innerHTML = `<input type="button" value="Clear search" style="float: left; margin-left: 10px;" onclick="$('#market-search').val('').keyup()">`;
const parent = searchInput.parentNode;
const el = searchInput.nextSibling;
const childNodes = tmpWrapper.childNodes;
for (let i = 0; i < childNodes.length; i++)
{
parent.insertBefore(childNodes[i], el);
}
// fix icon paths
const oldGetImagePath = window.getImagePath;
window.getImagePath = (itemVar) =>
{
if (itemVar == 'dragonFurnace')
{
return 'images/crafting/dragonFurnace.gif';
}
return oldGetImagePath(itemVar);
};
// auto focus the search input
const oldSelectItemToTradeDialog = window.selectItemToTradeDialog;
window.selectItemToTradeDialog = (sellOrBuy, slot) =>
{
oldSelectItemToTradeDialog(sellOrBuy, slot);
window.$('#id_search').focus();
};
}
/**
* improve level calculation
*/
let levelXp = new Array(maxLevelVirtual+1);
function calcLevelXp(level)
{
return level > 0 ? Math.round(Math.pow((level-1), 3 + ((level-1) / 200))) : 0;
}
function getLevelXp(level)
{
return levelXp[level-1] || calcLevelXp(level);
}
const getDynamicLevel = (function ()
{
const size = Math.pow(2, Math.ceil(Math.log2(maxLevel)));
let xpTree = new Array(size);
let levelTree = new Array(size);
const sizeVirtual = Math.pow(2, Math.ceil(Math.log2(maxLevelVirtual)));
let xpTreeVirtual = new Array(sizeVirtual);
let levelTreeVirtual = new Array(sizeVirtual);
createNode(xpTree, levelTree, 1, maxLevel, 0);
createNode(xpTreeVirtual, levelTreeVirtual, 1, maxLevelVirtual, 0);
function createNode(xpArray, levelArray, start, end, i)
{
const current = start + Math.pow(2, Math.floor(Math.log2(end - start + 1))) - 1;
xpArray[i] = getLevelXp(current);
levelArray[i] = current;
if (current - start > 0)
{
createNode(xpArray, levelArray, start, current-1, 2*i + 1);
}
if (end - current > 0)
{
createNode(xpArray, levelArray, current+1, end, 2*i + 2);
}
}
function getDynamicLevel(playerXP, useVirtual = false)
{
const isVirtual = window.virtualLevelsOn !== 0 && useVirtual === true;
const xpArray = isVirtual ? xpTreeVirtual : xpTree;
const levelArray = isVirtual ? levelTreeVirtual : levelTree;
let i = 0;
let level = 0;
while (xpArray[i] != null)
{
if (playerXP == xpArray[i])
{
return levelArray[i];
}
else if (playerXP < xpArray[i])
{
i = 2*i+1;
}
else if (playerXP > xpArray[i])
{
level = levelArray[i];
i = 2*i+2;
}
}
return level;
}
return getDynamicLevel;
})();
function getLevel(playerXP)
{
return getDynamicLevel(playerXP, false);
}
function getVirtualLevel(playerXP)
{
return getDynamicLevel(playerXP, true);
}
function getGlobalLevel()
{
return getDynamicGlobalLevel(false);
}
function getDynamicGlobalLevel(useVirtual = false)
{
return Math.floor(getDynamicLevel(parseInt(window.miningXp, 10), useVirtual))
+ Math.floor(getDynamicLevel(parseInt(window.craftingXp, 10), useVirtual))
+ Math.floor(getDynamicLevel(parseInt(window.brewingXp, 10), useVirtual))
+ Math.floor(getDynamicLevel(parseInt(window.merchantingXp, 10), useVirtual))
+ Math.floor(getDynamicLevel(parseInt(window.exploringXp, 10), useVirtual))
+ Math.floor(getDynamicLevel(parseInt(window.cookingXp, 10), useVirtual))
+ Math.floor(getDynamicLevel(parseInt(window.magicXp, 10), useVirtual))
;
}
function improveLevelCalculation()
{
if (!getSetting('useFastLevelCalculation'))
{
return;
}
for (var i = 1; i < maxLevelVirtual; i++)
{
levelXp[i-1] = calcLevelXp(i);
}
window.getLevel = getLevel;
window.getVirtualLevel = getVirtualLevel;
window.getGlobalLevel = getGlobalLevel;
}
/**
* fix inventory
*/
function fixInventory()
{
if (!getSetting('hideUnnecessaryPrice'))
{
return;
}
const tab = document.getElementById('gatherings-tab');
const coinImgs = tab.querySelectorAll('span[id^="item-"][id$="-box"] img[src="images/pic_coin.png"]');
for (let i = 0; i < coinImgs.length; i++)
{
const coinImg = coinImgs[i];
const price = coinImg.nextSibling;
if (price.nodeType == Node.TEXT_NODE && !/\d/.test(price.textContent))
{
const parent = coinImg.parentNode;
parent.removeChild(coinImg);
parent.removeChild(price);
}
}
}
/**
* fix machinery
*/
function getMachineCount(machine)
{
return window['binded' + machine[0].toUpperCase() + machine.substr(1)];
}
function getOilValueFromMachine(machine)
{
return (oilConsumption[machine] || 0) * getMachineCount(machine);
}
function updateRepairCost(machine)
{
const input = document.getElementById('machineryChosenPopup');
const repairCost = document.getElementById('repair-price-dialog');
if (!input || !repairCost)
{
return;
}
machine = machine || input.value;
const percent = window[machine + 'Repair'];
const cost = window.getRepairCost(machine, percent);
repairCost.textContent = formatNumber(cost);
}
function openOilDialogue(varname)
{
const gearOnPath = 'images/spinning-gear.gif';
const gearOffPath = 'images/spinning-gear-off.gif';
const oilArea = document.getElementById('oilUsage-area');
const oilValue = document.getElementById('oilUsage-value');
const repairArea = document.getElementById('machinery-repair-area');
let machine = varname.replace(/key-item-binded([^-]+)-box/, '$1');
machine = machine[0].toLowerCase() + machine.substr(1);
// machine name + count
const name = machineNames[machine];
const count = getMachineCount(machine);
const max = 10; // don't know if there is a machine with a different limit...
let title = document.getElementById('machinery-name');
if (!title)
{
title = document.createElement('h3');
title.style.marginTop = 0;
title.id = 'machinery-name';
const parent = document.getElementById('machinery-dialog');
parent.insertBefore(title, parent.firstChild);
}
title.innerHTML = `${name} <span style="float: right;font-size: 1.2rem;">${count}<span style="font-weight: normal;">/${max}</span><span></span></span>`;
// PROGRESS BAR
var hasRepair = window.bindedPromethiumWrench > 0;
if (machine == 'sandCollector')
{
// hide repair part (ensure, it is hidden)
repairArea.setAttribute('style', 'padding: 0; width: 0px; height: 0px; overflow: hidden; border: 0;');
}
else
{
// show repair part if available
repairArea.setAttribute('style', 'display: ' + (hasRepair ? 'block' : 'none') + ';');
const progressBar = document.getElementById('progress-bar-repair-opened');
const percent = window[machine + 'Repair'];
const bgColor = percent < 20 ? 'yellow' : (percent >= 50 ? 'lime' : 'yellow');
progressBar.style.backgroundColor = bgColor;
progressBar.style.width = percent + '%';
let repairButton = document.getElementById('repair-current-machine');
if (!repairButton)
{
repairButton = document.createElement('button');
repairButton.id = 'repair-current-machine';
repairButton.style.lineHeight = '24px';
repairButton.style.margin = '10px 5% 0';
repairButton.style.width = '90%';
repairButton.style.position = 'relative';
repairButton.innerHTML = `<img id="bindedPromethiumWrenchOrb-img" src="images/crafting/promethiumWrench.png" alt="workers" width="23px" height="23px" style="position: absolute; top: 3px; left: 13px;">Repair for <span id="repair-price-dialog"></span><img src="images/pic_coin.png" width="25px" height="25px" style="vertical-align: middle;">`;
repairButton.onclick = () =>
{
const machine = document.getElementById('machineryChosenPopup').value;
window.send('REPAIR_MACHINERY=' + machine);
};
const parent = document.getElementById('machinery-repair-area');
parent.appendChild(repairButton);
}
updateRepairCost(machine);
}
// END PROGRESS BAR
oilValue.innerHTML = window.getOilValueFromMachine(machine);
document.getElementById('machineryChosenPopup').value = machine;
const isOn = window[machine + 'AreOn'] == 1;
document.getElementById('myonoffswitch').checked = isOn;
document.getElementById('myonoffswitch-gear').src = isOn ? gearOnPath : gearOffPath;
oilArea.style.display = isOn ? '' : 'none';
window.$('#machinery-dialog').dialog(
{
width: 400
});
}
function fixMachinery()
{
const oldChangeSmeltingValue = window.changeSmeltingValue;
window.changeSmeltingValue = () =>
{
window.setSmeltingBarAgain(
window.barTypeSelectedToSmeltGlobal
, document.getElementById('smeltingAmountRequested')
);
};
const oldOpenFurnaceDialogue = window.openFurnaceDialogue;
window.openFurnaceDialogue = () =>
{
const ret = oldOpenFurnaceDialogue();
if (furnacePerc == 0)
{
window.changeSmeltingValue();
}
return ret;
};
const oldRapairMachinery = window.rapairMachinery;
window.rapairMachinery = () =>
{
oldRapairMachinery();
document.getElementById('perc-all-cost').innerHTML = window.getRepairCost('all', 0).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
};
const furnaceCapacaties = [0, 10, 30, 75, 150, 300, 500, 750, 1000, 1250];
function upgradeFurnaceOrb()
{
if (window.bindedUpgradeFurnaceOrb != 1)
{
return;
}
for (let i = 1; i < furnaceLevels.length; i++)
{
let furnaceType = furnaceLevels[i];
furnaceType = furnaceType[0].toUpperCase() + furnaceType.substr(1);
const capacity = 1.5 * furnaceCapacaties[i];
const box = document.getElementById('key-item-binded' + furnaceType + 'Furnace-box');
let textNode = box.lastChild;
if (textNode.nodeType !== Node.TEXT_NODE)
{
textNode = textNode.lastChild;
}
textNode.textContent = ' ' + formatNumber(capacity);
}
}
upgradeFurnaceOrb();
observe('bindedUpgradeFurnaceOrb', () => upgradeFurnaceOrb());
if (!getSetting('improveMachineryDialog'))
{
return;
}
window.getOilValueFromMachine = getOilValueFromMachine;
window.openOilDialogue = openOilDialogue;
observe(['drillRepair', 'crusherRepair', 'giantDrillRepair', 'roadHeaderRepair', 'bucketWheelExcavatorRepair', 'giantBWERepair'], () => updateRepairCost());
}
/**
* fix brewing
*/
const potionRequirements = {
'seedPotion': {
level: 5
, dottedGreenLeaf: 5
, redMushroom: 100
, greenLeaf: 1
}
};
let oldCanBrewItem;
function canBrewItem(command)
{
var requirements = potionRequirements[command];
if (!requirements)
{
return oldCanBrewItem(command);
}
for (var key in requirements)
{
if (key == 'level')
{
if (getLevel(brewingXp) < requirements.level)
{
return false;
}
}
else if (window[key] < requirements[key])
{
return false;
}
}
return true;
}
function fixBrewing()
{
oldCanBrewItem = window.canBrewItem;
window.canBrewItem = canBrewItem;
const marginFix = '5px 20px';
const potionItems = document.querySelectorAll('#brewing-tab [id$="Potion-box"] img.item-box-img');
for (let i = 0; i < potionItems.length; i++)
{
potionItems[i].style.margin = marginFix;
}
const smallImgItems = ['vial','enchantedVial','compost'];
for (let item of smallImgItems)
{
document.querySelector('#item-' + item + '-box img.item-box-img').style.margin = marginFix;
}
}
/**
* fix tabs
*/
const tabs2Fix = {
repair: {
name: 'Machinery'
, url: 'https://www.reddit.com/r/DiamondHunt/wiki/online/tabs/machinery'
}
, store: {
name: 'Market'
, url: 'https://www.reddit.com/r/DiamondHunt/wiki/online/tabs/market'
}
, 'npc-store': {
name: 'Game Shop'
, url: 'https://www.reddit.com/r/DiamondHunt/wiki/online/tabs/market/game'
}
, 'donor-store': {
name: 'Donor Shop'
, url: 'https://www.reddit.com/r/DiamondHunt/wiki/online/tabs/market/donor'
}
, 'player-store': {
name: 'Player Market'
, url: 'https://www.reddit.com/r/DiamondHunt/wiki/online/tabs/market/player'
}
, stats: {
name: 'Leaderboards'
, url: 'https://www.reddit.com/r/DiamondHunt/wiki/online/tabs/stats'
}
, coop: {
name: 'Group Tasks'
, url: 'https://www.reddit.com/r/DiamondHunt/wiki/online/tabs/coop'
}
, collectables: {
name: 'Collectables'
}
, miningEngineer: {
name: 'Mining Engineer'
}
, 'ach-explore': {
name: 'Exploring — Equipment'
, url: 'https://www.reddit.com/r/DiamondHunt/wiki/online/tabs/exploration#wiki_equipment'
}
};
function tabTitleLink2Span(title)
{
const span = title.parentNode;
span.appendChild(title.firstChild);
span.removeChild(title);
span.setAttribute('tooltip', '');
span.style.color = 'gold';
span.style.fontSize = '24pt';
}
function fixTabs()
{
function removeElement(el)
{
el.parentNode.removeChild(el);
}
/**
* some special treatment
*/
const achievementTitle = document.querySelector('#ach-tab a');
tabTitleLink2Span(achievementTitle);
const npcH1 = document.querySelector('#npc-store-tab h1');
removeElement(npcH1);
const vendorBr = document.querySelector('#vendor-tab > br:first-child');
removeElement(vendorBr);
const vendorTitle = document.querySelector('#vendor-tab a');
tabTitleLink2Span(vendorTitle);
const wizardBr = document.querySelector('#wizard-tab > br:first-child');
removeElement(wizardBr);
const achTitle = document.querySelector('#archaeology-tab a');
achTitle.title = '';
const achCraftTitle = document.querySelector('#archaeology-crafting-tab a');
achCraftTitle.textContent = 'Exploring — Crafting'.toUpperCase();
tabTitleLink2Span(achCraftTitle);
const cookingTitle = document.querySelector('#cooking-tab a');
cookingTitle.textContent = 'Exploring — Cooking'.toUpperCase();
cookingTitle.title = '';
cookingTitle.parentNode.setAttribute('tooltip', 'Open Wiki');
const magicSpellbookTitle = document.querySelector('#spellbook-tab a');
magicSpellbookTitle.textContent = 'Magic — spellbook'.toUpperCase();
const magicCraftTitle = document.querySelector('#magiccrafting-tab a');
magicCraftTitle.textContent = 'Magic — Crafting'.toUpperCase();
tabTitleLink2Span(magicCraftTitle);
removeElement(document.querySelector('#repair-tab > br:last-child'));
removeElement(document.querySelector('#repair-tab > br:last-child'));
removeElement(document.querySelector('#miningEngineer-tab br'));
removeElement(document.querySelector('#brewing-tab br'));
removeElement(document.querySelector('#archaeology-tab br'));
removeElement(document.querySelector('#ach-explore-tab br'));
removeElement(document.querySelector('#ach-explore-tab br'));
const archCraftBr = document.querySelector('#archaeology-crafting-tab br');
archCraftBr.parentNode.insertBefore(document.createElement('br'), archCraftBr);
removeElement(document.querySelector('#cooking-tab br'));
removeElement(document.querySelector('#cooking-tab br'));
removeElement(document.querySelector('#cooking-tab br'));
removeElement(document.querySelector('#magic-tab br'));
removeElement(document.querySelector('#magiccrafting-tab br'));
removeElement(document.querySelector('#magiccrafting-tab br'));
for (let i = 0; i < 10; i++)
{
removeElement(document.querySelector('#magiccrafting-tab > span + br'));
}
removeElement(document.querySelector('#store-tab br'));
removeElement(document.querySelector('#player-store-tab br'));
removeElement(document.querySelector('#stats-tab br'));
removeElement(document.querySelector('#stats-tab br'));
removeElement(document.querySelector('#grouptasks-createorjoin br'));
removeElement(document.querySelector('#grouptasks-notstarted br'));
removeElement(document.querySelector('#grouptasks-notstarted br'));
removeElement(document.querySelector('#grouptasks-started br'));
for (let key in tabs2Fix)
{
const tab = tabs2Fix[key];
const tabEl = document.getElementById(key + '-tab');
const tmpEl = document.createElement('tmpWrapper');
let html = '<center>';
if (tab.url)
{
html += `<span class="activate-tooltip">
<a class="title-link" href="${tab.url}" target="_blank" title="Open Wiki">${tab.name.toUpperCase()}</a>
</span>`;
}
else
{
html += `<span class="activate-tooltip" style="color: gold; font-size: 24pt;">
${tab.name.toUpperCase()}
</span>`;
}
html += '</center><br>';
tmpEl.innerHTML = html;
let el = tabEl.firstElementChild;
for (let i = tmpEl.children.length-1; i >= 0; i--)
{
const child = tmpEl.children[i];
tabEl.insertBefore(child, el);
el = child;
}
}
}
/**
* hide crafting recipes
*/
const recipes = {
shovel: ['shovel']
, promethiumWrench: ['promethiumWrench', 'bindedPromethiumWrench']
, glassBlowingPipe: ['glassBlowingPipe', 'bindedGlassBlowingPipe']
, oilPipe: ['oilPipe', 'bindedOilPipe']
, planter: ['planter', 'bindedPlanter']
, trowel: ['trowel', 'bindedTrowel']
, shootingStarCrystal: ['shootingStarCrystal', 'bindedShootingStarCrystal']
, brewingKit: ['brewingKit', 'brewingKitBinded']
, rocket: ['rocket', 'bindedRocket']
, redPumpJack: ['redPumpJack', 'bindedRedPumpJack']
, explorersBrush: ['explorersBrush', 'bindedExplorersBrush']
, oilFactory: ['oilFactory', 'bindedOilFactory']
, robot: ['robot', 'bindedRobot']
, oilRefinery: ['oilRefinery', 'bindedOilRefinery']
, superRobot: ['superRobot', 'bindedSuperRobot']
// , fishingRod: ['bronzeRod', 'ironRod', 'goldRod', 'promethiumRod', 'fishingRod', 'dragonFishingRod', 'bindedDragonFishingRod']
, fishingRod: ['fishingRod', 'dragonFishingRod', 'bindedDragonFishingRod']
, fishingBoat: ['fishingBoat', 'bindedFishingBoat']
, largeFishingBoat: ['largeFishingBoat', 'bindedLargeFishingBoat']
};
function hideCraftingRecipes()
{
if (!getSetting('hideSomeCraftRecipes'))
{
return;
}
(function hideFurnaceRecipes(init = false)
{
let maxFurnaceLevel = parseInt(window.bindedFurnaceLevel, 10);
let keys2Observe = ['bindedFurnaceLevel'];
for (let i = furnaceLevels.length-1; i >= 0; i--)
{
const varName = furnaceLevels[i] + 'Furnace';
if (window[varName] > 0)
{
maxFurnaceLevel = Math.max(maxFurnaceLevel, i);
}
const row = document.getElementById('craft-' + furnaceLevels[i] + 'Furnace');
if (row)
{
const hide = i <= maxFurnaceLevel;
row.style.display = hide ? 'none' : '';
keys2Observe.push(varName);
}
}
if (init)
{
observe(keys2Observe, () => hideFurnaceRecipes(false));
}
})(true);
function hideRecipe(key, nameList, init = false)
{
const hide = nameList.some(name => window[name] != 0);
document.getElementById('craft-' + key).style.display = hide ? 'none' : '';
if (init)
{
observe(nameList, () => hideRecipe(key, nameList, false));
}
}
for (let key in recipes)
{
hideRecipe(key, recipes[key], true);
}
// exploring - crafting
(function hideOvenRecipes(init = false)
{
let maxOvenLevel = -1;
let keys2Observe = [];
for (let i = ovenLevels.length-1; i >= 0; i--)
{
const type = ovenLevels[i];
const ovenName = type + 'Oven';
const bindedOvenName = 'binded' + ovenName[0].toUpperCase() + ovenName.substr(1);
if (window[ovenName] > 0 || window[bindedOvenName] > 0)
{
maxOvenLevel = Math.max(maxOvenLevel, i);
}
const row = document.getElementById('craft-' + type + 'Oven');
if (row)
{
const hide = maxOvenLevel >= i;
row.style.display = hide ? 'none' : '';
keys2Observe.push(ovenName, bindedOvenName);
}
}
if (init)
{
observe(keys2Observe, () => hideOvenRecipes(false));
}
})(true);
// exploring - equipment
function hideEquipmentRecipe(key, type, init = false)
{
let highestLevel = parseInt(window[key + 'SlotId'], 10) - 1;
for (let i = barTypes.length-1; i >= 0; i--)
{
const bar = barTypes[i];
if (window[bar + type] > 0)
{
highestLevel = Math.max(highestLevel, i);
}
const row = document.getElementById('craft-' + bar + type);
if (row && highestLevel >= i)
{
row.style.display = 'none';
}
}
if (init)
{
observe(key + 'SlotId', () => hideEquipmentRecipe(key, type, false));
for (let i = barTypes.length-1; i >= 0; i--)
{
const bar = barTypes[i];
observe(bar + type, () => hideEquipmentRecipe(key, type, false));
}
}
}
const equipmentTypes = {
'weapon': 'Sword'
, 'helmet': 'Helmet'
, 'body': 'Body'
, 'leg': 'Legs'
};
for (let key in equipmentTypes)
{
hideEquipmentRecipe(key, equipmentTypes[key], true);
}
// magic - crafting
const magicRodTypes = ['gold', 'promethium', 'runite', 'dragon'];
(function hideWandRecipe(init = false)
{
let maxWandLevel = -1;
let keys2Observe = [];
for (let i = magicRodTypes.length-1; i >= 0; i--)
{
const type = magicRodTypes[i];
const wandName = type + 'Wand';
const bindedWandName = 'binded' + wandName[0].toUpperCase() + wandName.substr(1);
if (window[wandName] > 0 || window[bindedWandName] > 0)
{
maxWandLevel = Math.max(maxWandLevel, i);
}
const wandRow = document.getElementById('craft-' + type + 'Wand');
if (wandRow)
{
const hide = maxWandLevel >= i;
wandRow.style.display = hide ? 'none' : '';
keys2Observe.push(wandName, bindedWandName);
}
}
if (init)
{
observe(keys2Observe, () => hideWandRecipe(false));
}
})(true);
(function hideStaffRecipe(init = false)
{
let maxStaffLevel = -1;
let keys2Observe = [];
for (let i = magicRodTypes.length-1; i >= 0; i--)
{
const type = magicRodTypes[i];
const staffName = type + 'Staff';
const bindedStaffName = 'binded' + staffName[0].toUpperCase() + staffName.substr(1);
if (window[staffName] > 0 || window[bindedStaffName] > 0)
{
maxStaffLevel = Math.max(maxStaffLevel, i);
}
const staffRow = document.getElementById('craft-' + type + 'Staff');
if (staffRow)
{
const hide = maxStaffLevel >= i;
staffRow.style.display = hide ? 'none' : '';
keys2Observe.push(staffName, bindedStaffName);
}
}
if (init)
{
observe(keys2Observe, () => hideWandRecipe(false));
}
})(true);
}
/**
* hide equipment
*/
const equipmentId2Type = {
general: ['', 'Bronze', 'Iron', 'Silver', 'Gold', 'Promethium', 'Runite', 'Dragon']
, amulet: ['', 'Amulet of the Sea', 'Moonstone Amulet'/*??? TBD*/, 'Enchanted Amulet of the Sea', 'Dragon Amulet']
, shield: ['', 'Ancient Shield']
, ring: ['', 'Coin Ring', 'Pure Water Ring', 'Lava Ring']
, secondRing: ['', 'Looting Gloves']
};
const equipmentTypes = {
'weapon': 'Sword'
, 'helmet': 'Helmet'
, 'body': 'Body'
, 'leg': 'Legs'
};
const equipmentTypeList = ['helmet', 'amulet', 'weapon', 'body', 'shield', 'ring', 'leg', 'secondRing'];
const equipmentLevels = ['bronze', 'iron', 'silver', 'gold', 'promethium', 'runite', 'dragon'];
const equipmentType2Name = {
'weapon': 'sword'
, 'leg': 'legs'
};
function setEquippedList(listCell, init)
{
let keys2Observe = [];
let list = [];
for (let type of equipmentTypeList)
{
const id = parseInt(window[type + 'SlotId'], 10);
keys2Observe.push(type + 'SlotId');
type = equipmentType2Name[type] || type;
if (!equipmentId2Type.hasOwnProperty(type))
{
list.push(equipmentId2Type.general[id] + ' ' + type[0].toUpperCase() + type.substr(1));
}
else
{
list.push(equipmentId2Type[type][id]);
}
}
listCell.textContent = list.filter(str => str != '').join(', ');
if (init)
{
for (let key of keys2Observe)
{
observe(key, () => setEquippedList(listCell, false));
}
}
}
function examineEquipmentRecipes(key, type, init = false)
{
const currentLevel = parseInt(window[key + 'SlotId'], 10);
// hide not more than gold equipment
for (let i = 0; i < equipmentLevels.length; i++)
{
const el = document.getElementById('item-' + equipmentLevels[i] + type + '-box');
const hide = i < 4 && i < currentLevel;
if (el)
{
el.parentNode.style.display = hide ? 'none' : '';
}
}
if (init)
{
observe(key + 'SlotId', () => examineEquipmentRecipes(key, type, false));
}
}
function hideEquipment()
{
const table = document.querySelector('#ach-explore-tab table.equipement-area-table');
const row = table.insertRow(-1);
row.style.borderTop = '1px dashed';
const nameCell = row.insertCell(-1);
nameCell.style.verticalAlign = 'top';
nameCell.textContent = 'Equipped:';
const listCell = row.insertCell(-1);
listCell.colSpan = 2;
listCell.textContent = '';
setEquippedList(listCell, true);
if (!getSetting('hideEquipment'))
{
return;
}
for (let key in equipmentTypes)
{
examineEquipmentRecipes(key, equipmentTypes[key], true);
}
}
/**
* improve dialog buttons
*/
function improveDialogBtns()
{
if (!getSetting('improveDialogBtns'))
{
return;
}
const oldOpenDialogue = window.openDialogue;
window.openDialogue = (title, message, yesButtonVal) =>
{
const [okBtn, cancelBtn] = document.querySelectorAll('#dialog #buttonCommandYes ~ input[type="button"]');
// restore default state
const empty = yesButtonVal == null || yesButtonVal == '';
okBtn.style.display = empty ? 'none' : '';
okBtn.value = 'OK';
cancelBtn.value = empty ? 'Close' : 'Cancel';
if (/stardust/i.test(title))
{
okBtn.value = 'Smash it';
}
else if (/bind/i.test(title))
{
okBtn.value = 'Bind';
}
else if (/drink/i.test(title))
{
okBtn.value = 'Drink';
}
else if (/^EXPLORE/.test(yesButtonVal))
{
okBtn.value = 'Start expedition';
}
return oldOpenDialogue(title, message, yesButtonVal);
};
const oldOpenDialogueWidth = window.openDialogueWidth;
window.openDialogueWidth = (title, message, yesButtonVal, widthWanted) =>
{
const [okBtn, cancelBtn] = document.querySelectorAll('#dialog #buttonCommandYes ~ input[type="button"]');
// restore default state
const empty = yesButtonVal == null || yesButtonVal == '';
okBtn.style.display = empty ? 'none' : '';
okBtn.value = 'OK';
cancelBtn.value = empty ? 'Close' : 'Cancel';
return oldOpenDialogueWidth(title, message, yesButtonVal, widthWanted);
};
const oldClicksKeyItem = window.clicksKeyItem;
window.clicksKeyItem = (varname) =>
{
oldClicksKeyItem(varname);
if (varname == 'key-item-bindedRocket-box' && rocketTimer == 0)
{
const [okBtn, cancelBtn] = document.querySelectorAll('#dialog #buttonCommandYes ~ input[type="button"]');
okBtn.value = 'Start rocket';
const textEl = document.querySelector('#dialog #dialog-text');
textEl.removeChild(textEl.lastChild);
textEl.removeChild(textEl.lastChild);
textEl.removeChild(textEl.lastChild);
}
};
const oldOpenFurnaceDialogue = window.openFurnaceDialogue;
window.openFurnaceDialogue = () =>
{
oldOpenFurnaceDialogue();
if (furnacePerc > 0)
{
const [okBtn, cancelBtn] = document.querySelectorAll('#dialog #buttonCommandYes ~ input[type="button"]');
okBtn.value = 'Cancel smelting';
cancelBtn.value = 'Close';
}
};
const oldOpenAreaDialogue = window.openAreaDialogue;
window.openAreaDialogue = () =>
{
oldOpenAreaDialogue();
if (parseInt(exploringTimer) > 0)
{
const [okBtn, cancelBtn] = document.querySelectorAll('#dialog #buttonCommandYes ~ input[type="button"]');
okBtn.value = 'Cancel trip';
cancelBtn.value = 'Close';
}
};
}
/**
* expand equipment
*/
function expandEquipment()
{
if (!getSetting('expandEquipment'))
{
return;
}
const equipmentRows = document.querySelectorAll('tr[onclick^="openCraftSwordDialogue"]');
const rowParent = equipmentRows[0].parentNode;
let newRows = [];
for (let i = 0; i < equipmentRows.length; i++)
{
const row = equipmentRows[i];
const type = row.getAttribute('onclick').replace(/openCraftSwordDialogue\('([^']+)'\);/, '$1');
const levels = row.cells[2].textContent.split('/');
const barCosts = row.cells[3].getAttribute('tooltip').replace(/\D*$/, '').split('/');
for (let i = 0; i < barTypes.length; i++)
{
const bar = barTypes[i];
const newRow = row.cloneNode(true);
newRow.id = 'craft-' + bar + type;
newRow.setAttribute('onclick', '');
newRow.cells[0].textContent = bar[0].toUpperCase() + bar.substr(1) + ' ' + type;
newRow.cells[1].firstElementChild.src = 'images/exploring/equipement/' + bar + type + '.png';
newRow.cells[2].textContent = levels[i];
newRow.cells[3].firstElementChild.src = 'images/minerals/' + bar + 'Bar.png';
newRow.cells[3].lastChild.textContent = ' ' + barCosts[i];
((item) =>
{
newRow.addEventListener('click', () => window.craftItem(item));
})(bar + type);
newRows.push({
level: parseInt(levels[i], 10)
, row: newRow
});
}
rowParent.removeChild(row);
}
newRows = newRows.sort((a, b) => a.level - b.level);
// insert new rows into table
const rows = rowParent.rows;
let idx = 0;
for (let i = 0; i < rows.length && idx < newRows.length; i++)
{
const row = rows[i];
if (row.getElementsByTagName('th').length > 0)
{
continue;
}
const thisLevel = parseInt(row.cells[2].textContent, 10);
while (newRows[idx] && newRows[idx].level < thisLevel)
{
rowParent.insertBefore(newRows[idx].row, row);
idx++;
}
}
for (; idx < newRows.length; idx++)
{
rowParent.appendChild(newRows[idx].row);
}
}
/**
* apply new item style
*/
function applyNewItemStyle()
{
if (!getSetting('applyNewItemStyle'))
{
return;
}
// change how the items are styled
const style = document.createElement('style');
style.innerHTML = `
span[class^="inventory-item-box"],
#vendor-tab span.shop-box,
#ach-tab span.shop-box-ach,
div[id$="-store-tab"] span.shop-box,
span.shop-box-ach
{
position: relative;
}
span[class^="inventory-item-box"] img.item-box-img,
#vendor-tab img[id^="vendor-item-img"],
#ach-tab span.shop-box-ach img:first-of-type,
div[id$="-store-tab"] span.shop-box img:first-of-type,
#grp-shop-tab span.shop-box-ach img:first-of-type
{
position: absolute;
margin: 0 !important;
}
img.item-box-img[height="30px"] { top: 52.5px; }
img.item-box-img[height="55px"] { top: 40px; }
img.item-box-img[height="60px"] { top: 37.5px; }
img.item-box-img[height="70px"] { top: 32.5px; }
img.item-box-img[height="75px"] { top: 30px; }
img.item-box-img[height="80px"] { top: 27.5px; }
img.item-box-img[height="85px"] { top: 25px; }
img.item-box-img[height="90px"] { top: 20px; }
img.item-box-img[height="100px"] { top: 9px; }
span[id^="item-binded"] > img.item-box-img[height="100px"] { top: 17.5px; }
img.item-box-img[height="110px"] { top: 12.5px; }
img.item-box-img[width="55px"] { left: 42.5px; }
img.item-box-img[width="60px"] { left: 40px; }
img.item-box-img[width="70px"] { left: 35px; }
img.item-box-img[width="75px"] { left: 32.5px; }
img.item-box-img[width="80px"] { left: 30px; }
img.item-box-img[width="90px"] { left: 25px; }
img.item-box-img[width="100px"] { left: 20px; }
img.item-box-img[width="110px"] { left: 15px; }
img.item-box-img[width="120px"] { left: 10px; }
span[class^="inventory-item-box"] img.item-box-img[height="60px"][width="60px"] { transform: scale(1.2); }
span[class^="inventory-item-box"] img.item-box-img[height="70px"][width="80px"] { transform: scale(1.2); }
/* this is a special case (converting items into stardust) */
#wizard-tab img.item-box-img[height="50px"] { top: 22px; }
#wizard-tab img.item-box-img[width="50px"] { left: 45px; }
#vendor-tab img[id^="vendor-item-img"]
{
transform: scale(1.1);
}
/* height: 155px */
img[id^="vendor-item-img"][height="85x"] { top: 35px; }
/* width: 150px (110px + 40px) */
img[id^="vendor-item-img"][width="80px"] { left: 35px; }
span[class^="inventory-item-box"] span[id$="mount"],
#ancientCrystalChargesSpan,
#vendor-tab span.box-title,
#ach-tab span[id^="cost-ach-"][id$="AchUpgrade"],
div[id$="-store-tab"] span[id$="-cost"],
#grp-shop-tab span[id$="-cost"]
{
background-color: black;
border-top: 1px solid rgba(255, 255, 255, 0.5);
color: white !important;
font-weight: normal;
margin: 0 !important;
padding: 3px;
text-align: center;
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
span[class^="inventory-item-box"] span[id$="mount"]:not(#energy-amount),
#vendor-tab span.box-title
{
padding-right: 9px;
}
span[class^="inventory-item-box"] span[id$="mount"]:not(#energy-amount)::before,
#vendor-tab span.box-title::before
{
content: '${String.fromCharCode(215)}';
margin-right: 3px;
}
#fishfarmer-img
{
top: 18px;
}
#hasMapOfTheSea-fishermen
{
position: absolute;
bottom: 3px;
left: 3px;
}
span[class^="inventory-item-box"] span[id$="-price"],
#vendor-tab span[id^="vendor-item-cost"]
{
font-weight: normal;
padding-left: 20px;
position: relative;
top: 1px;
}
#vendor-tab span[id^="vendor-item-cost"]
{
font-size: inherit !important;
}
span[class^="inventory-item-box"] span[id$="-price"]:empty,
#sandstone-price,
#moonStone-price,
#glass-price,
#brewingKitBinded-price,
#stripedLeaf-price,
#greenMushroom-price,
#whaleTooth-price,
#snapeGrass-price,
#strangeLeaf-price,
#pureWaterPotion-price,
#cactusWater-price,
#ghostRemains-price,
span[id$="Potion-price"]
{
visibility: hidden;
}
span[class^="inventory-item-box"] span[id$="-price"]::before,
#vendor-tab span[id^="vendor-item-cost"]::before,
div[id$="-store-tab"] span[id$="-cost"]::before,
#grp-shop-tab span[id$="-cost"]::before
{
content: '';
display: inline-block;
width: 20px;
height: 20px;
position: absolute;
left: 0;
background-image: url('images/pic_coin.png');
background-size: 20px 20px;
}
#shop-ghostPirates-cost::before
{
background-image: url('images/pic_coin2.png');
}
#grp-shop-tab span[id$="-cost"]::before
{
background-image: url('images/icons/groupTaskTokens.png');
}
#grp-shop-tab #grp-chests-badge-cost::before
{
background-image: url('images/icons/groupTaskBadge4.png');
}
span[class^="inventory-item-box"] img[src="images/pic_coin.png"],
#vendor-tab img[src="images/pic_coin.png"],
#npc-store-tab img[src^="images/pic_coin"],
#npc-store-tab img[src="images/icons/stats.png"],
#npc-store-tab img[src="images/crafting/anyOrb.png"],
#npc-store-tab img[src="images/spinning-gear-off.gif"],
#donor-store-tab img ~ img[src="images/donor_coin.png"],
#donor-store-tab span[id$="-cost"] img[src="images/donor_coin.png"],
#grp-shop-tab img[src^="images/icons/groupTask"][id^="group-"],
#grp-shop-tab #grp-shop-badge-price-img
{
display: none;
}
#ach-tab span[id^="cost-ach-"][id$="AchUpgrade"]::before
{
content: '';
display: inline-block;
width: 20px;
height: 20px;
position: absolute;
top: 2px;
left: 4px;
background-image: url('images/shop/ach.png');
background-size: 20px 20px;
}
#ach-tab span.box-title
{
font-size: 14pt;
font-weight: normal;
position: relative;
top: 4px;
}
#ach-tab span.shop-box-ach img:first-of-type[height="60px"] { top: 47.5px; }
#ach-tab span.shop-box-ach img:first-of-type[height="80px"] { top: 37.5px; }
#ach-tab span.shop-box-ach img:first-of-type[width="55px"] { left: 47.5px; }
#ach-tab span.shop-box-ach img:first-of-type[width="80px"] { left: 35px; }
#ach-tab span.shop-box-ach img[src="images/shop/ach.png"]
{
display: none;
}
#ach-tab span.shop-box-ach img[src="images/division/check.png"],
#grp-shop-tab img[src="images/division/check.png"]
{
position: absolute;
bottom: 5px;
left: calc(50% - 10px);
}
#npc-store-tab span.box-title,
#donor-store-tab span.box-title,
#grp-shop-tab span.box-title
{
font-size: 1.1rem;
font-weight: bold;
margin: 0;
padding: 4px;
position: absolute;
left: 0;
right: 0;
}
#donor-store-tab span.box-title
{
font-size: 1.02rem;
}
#shop-coop-level-cost,
#shop-miningEngineer-machines-cost,
#grp-shop-tab #grp-chests-badge-cost
{
bottom: 26px;
}
#shop-coop-level-cost::before
{
background-image: url('images/icons/stats.png');
left: 1px;
}
#shop-wizard-cost::before
{
background-image: url('images/crafting/anyOrb.png');
}
#shop-miningEngineer-machines-cost::before
{
background-color: white;
background-image: url('images/spinning-gear-off.gif');
}
#npc-store-tab img:first-of-type[height="60px"] { top: 47.5px; }
#npc-store-tab img:first-of-type[height="65px"] { top: 45px; }
#npc-store-tab img:first-of-type[height="70px"] { top: 42.5px; }
#npc-store-tab img:first-of-type[height="80px"] { top: 37.5px; }
#npc-store-tab img:first-of-type[height="85x"] { top: 35px; }
#npc-store-tab img:first-of-type[height="100x"] { top: 27.5px; }
#npc-store-tab img:first-of-type[width="60px"] { left: 45px; }
#npc-store-tab img:first-of-type[width="65px"] { left: 42.5px; }
#npc-store-tab img:first-of-type[width="80px"] { left: 35px; }
#npc-store-tab img:first-of-type[width="100px"] { left: 25px; }
#donor-store-tab img:first-of-type[height="80px"] { top: 37.5px; }
#donor-store-tab img:first-of-type[width="80px"] { left: 35px; }
#donor-store-tab span[id$="-cost"]::before
{
background-image: url('images/donor_coin.png');
}
#grp-shop-tab img[id^="grp-shop-"][height="80px"] { top: 37.5px; }
#grp-shop-tab img[id^="grp-shop-"][width="80px"] { left: 35px; }
#grp-shop-tab img[id^="grp-shop-"][width="90px"] { left: 30px; }
#grp-shop-tab img[src="images/division/check.png"] + span
{
display: none;
}
`;
document.head.appendChild(style);
// remove line breaks
const brs = document.querySelectorAll(
'[class^="inventory-item-box"] br'
+ ', span.shop-box br'
+ ', #ach-tab span.shop-box-ach br'
+ ', #grp-shop-tab span.shop-box-ach br'
);
let i = 0;
while (brs[i] != null)
{
brs[i++].parentNode && brs[--i].parentNode.removeChild(brs[i]);
}
// give the emerald image the correct class name
const emeraldAmount = document.getElementById('emeraldAmount');
const previous = emeraldAmount && emeraldAmount.previousElementSibling;
previous && previous.classList.add('item-box-img');
// wrap some requirements in npc-shop
const shopBoxes = document.querySelectorAll('div[id$="-store-tab"] span.shop-box');
for (let i = 0; i < shopBoxes.length; i++)
{
const box = shopBoxes[i];
const children = box.childNodes;
let foundImg = false;
let wrapper;
const idList = {
'shop-coopUnlocked-box': ['shop-coop-cost', 'shop-coop-level-cost']
, 'shop-hasVendor-box': ['shop-vendor-cost']
, 'shop-wizard-box': ['shop-wizard-cost']
, 'shop-achShop-box': ['shop-achShop-cost']
, 'shop-miningEngineer-box': ['shop-miningEngineer-cost', 'shop-miningEngineer-machines-cost']
, 'donor-shop-hasExtraOfflineTimer-box': ['shop-extraOfflineTimer-cost']
, '': ['shop-offlineTimer-cost']
}[box.id] || [];
for (let j = 0; j < children.length; j++)
{
const child = children[j];
if (!foundImg && child.tagName == 'IMG')
{
foundImg = true;
}
else if (foundImg && child.nodeType == Node.TEXT_NODE)
{
wrapper = document.createElement('span');
wrapper.id = idList.shift() || '';
box.insertBefore(wrapper, child);
wrapper.appendChild(child);
}
else if (foundImg && wrapper != null)
{
wrapper.appendChild(child);
j--;
}
}
}
// wrap some requirements in group shop
const grpBoxes = document.querySelectorAll('#grp-shop-tab span.shop-box-ach');
const idList = ['grp-badge-cost', 'grp-more-points-cost', 'grp-eels-cost', 'grp-promethium-cost', 'grp-chests-cost', 'grp-chests-badge-cost', 'grp-gloves-cost'];
for (let i = 0; i < grpBoxes.length; i++)
{
const box = grpBoxes[i];
const children = box.childNodes;
let foundImg = false;
let wrapper;
for (let j = 0; j < children.length; j++)
{
const child = children[j];
if (!foundImg && child.tagName == 'IMG')
{
foundImg = true;
}
else if (foundImg && wrapper == null)
{
wrapper = document.createElement('span');
wrapper.id = idList.shift();
box.insertBefore(wrapper, child);
wrapper.appendChild(child);
}
else if (foundImg && wrapper != null)
{
if (child.nodeName == 'IMG')
{
box.insertBefore(child, wrapper);
wrapper = null;
}
else
{
wrapper.appendChild(child);
j--;
}
}
}
}
}
/**
* apply new key item style
*/
function applyNewKeyItemStyle()
{
if (!getSetting('applyNewKeyItemStyle'))
{
return;
}
// change how key items and machinery is styled
const style = document.createElement('style');
style.innerHTML = `
span[class$="-inventory-item-box"]
{
position: relative;
}
span.item-box-title
{
color: blue;
font-weight: bold;
padding: 4px 8px;
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 10;
}
span.item-box-title > img[src="images/oil.png"]
{
display: block;
margin: 0 auto;
}
span.item-box-title > img[src^="images/spinning-gear"]
{
margin-right: 3px;
margin-left: -10px;
}
span[class$="-inventory-item-box"] > img
{
position: absolute;
}
/* heights */
span[class$="-inventory-item-box"] > img[height="80px"] { top: 60px; }
span[class$="-inventory-item-box"] > img[height="90px"] { top: 55px; }
span[class$="-inventory-item-box"] > img[height="100px"] { top: 50px; }
span[class$="-inventory-item-box"] > img[height="110px"] { top: 45px; }
span[class$="-inventory-item-box"] > img[height="120px"] { top: 40px; }
span[class$="-inventory-item-box"] > img[height="130px"] { top: 35px; }
span[class$="-inventory-item-box"] > img[height="140px"] { top: 30px; }
/* widths */
span[class$="-inventory-item-box"] > img[width="70px"] { left: 35px; }
span[class$="-inventory-item-box"] > img[width="80px"] { left: 30px; }
span[class$="-inventory-item-box"] > img[width="90px"] { left: 25px; }
span[class$="-inventory-item-box"] > img[width="100px"] { left: 20px; }
span[class$="-inventory-item-box"] > img[width="110px"] { left: 15px; }
span[class$="-inventory-item-box"] > img[width="120px"] { left: 10px; }
#key-item-bindedGlassBlowingPipe-box img
{
top: 75px;
}
span.ghostPipe-wrapper
{
display: flex;
flex-wrap: wrap;
position: absolute;
top: 66px;
left: 19px;
width: 102px;
}
span.ghostPipe-wrapper > img
{
box-shadow: 0 0 2px black;
margin: 2px;
}
span.text-wrapper
{
background-color: black;
border-top: 1px solid rgba(255, 255, 255, 0.5);
color: white;
font-weight: normal;
line-height: 22px;
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
span.text-wrapper span[id$="Amount"],
#level-global-4
{
font-weight: bold;
}
#key-item-handheldOilPump-box span.text-wrapper
{
display: none;
}
span.text-wrapper img
{
margin-right: 3px;
}
span.text-wrapper .small-perc-bar
{
background-color: black;
border: 0;
margin: 0;
padding: 0;
position: absolute;
top: -11px;
left: 0;
right: 0;
width: auto;
}
span.text-wrapper .small-perc-bar-inner
{
background-color: rgba(0, 210, 0, 1) !important;
}
`;
document.head.appendChild(style);
const brs = document.querySelectorAll('span[class$="-inventory-item-box"] br');
let i = 0;
while (brs[i] != null)
{
const br = brs[i];
const parent = br.parentNode;
if (!parent)
{
i++;
continue;
}
if (parent.classList.contains('item-box-title'))
{
parent.insertBefore(document.createTextNode(' '), br);
}
parent.removeChild(br);
}
const spans = document.querySelectorAll('span[class$="-inventory-item-box"]');
let ghostWrapper;
for (let i = 0; i < spans.length; i++)
{
const span = spans[i];
const childs = span.childNodes;
let wrapper;
let foundImg = false;
for (let j = 0; j < childs.length; j++)
{
const child = childs[j];
if (!foundImg && child.tagName == 'IMG')
{
if (/ghostPipeHolder/.test(child.id))
{
if (!ghostWrapper)
{
ghostWrapper = document.createElement('span');
ghostWrapper.className = 'ghostPipe-wrapper';
child.parentNode.insertBefore(ghostWrapper, child);
j++;
}
ghostWrapper.appendChild(child);
j--;
if (child.id == 'ghostPipeHolder6')
{
foundImg = true;
}
}
else
{
foundImg = true;
}
}
else if (foundImg)
{
if (!wrapper)
{
wrapper = document.createElement('span');
wrapper.className = 'text-wrapper';
span.insertBefore(wrapper, child);
j++;
}
wrapper.appendChild(child);
j--;
}
}
if (wrapper && wrapper.textContent == '')
{
wrapper.parentNode.removeChild(wrapper);
}
}
}
/**
* hide recipes of maxed machinery
*/
function hideMaxRecipes()
{
if (!getSetting('hideMaxRecipes'))
{
return;
}
function hideMachineRecipe(key, max, init)
{
const bindedKey = 'binded' + key[0].toUpperCase() + key.substr(1);
const row = document.getElementById('craft-' + key);
if (row)
{
const amount = parseInt(window[key], 10) + parseInt(window[bindedKey], 10);
const hide = amount >= max();
row.style.display = hide ? 'none' : '';
if (init)
{
observe(key, () => hideMachineRecipe(key, max, false));
observe(bindedKey, () => hideMachineRecipe(key, max, false));
}
}
}
const machinery = ['drill', 'crusher', 'giantDrill', 'sandCollector', 'roadHeader', 'bucketWheelExcavator', 'giantBWE'];
for (let key of machinery)
{
hideMachineRecipe(key, () => 10, true);
}
// handle pump jacks with its upgrades
function calcPumpjackMax()
{
let maxPumpjacks = 10;
if (window.bindedUpgradePumpJackOrb == 1)
{
maxPumpjacks += 5;
}
if (window.bindedGreenPumpjackOrb == 1)
{
maxPumpjacks += 10;
}
return maxPumpjacks;
}
hideMachineRecipe('pumpJack', calcPumpjackMax, true);
observe('bindedUpgradePumpJackOrb', () => hideMachineRecipe('pumpJack', calcPumpjackMax, false));
observe('bindedGreenPumpjackOrb', () => hideMachineRecipe('pumpJack', calcPumpjackMax, false));
}
/**
* fix magic
*/
const essenceMultiplier = {
mineral: {
stone: 20e6
, copper: 10e6
, tin: 10e6
, iron: 5e6
, silver: 2e6
, gold: 1e6
, quartz: 100e3
, flint: 50e3
, marble: 10e3
, titanium: 5e3
, promethium: 100
, runite: 2
}
, oil: {
oil: 5e7
, rocketFuel: 1
}
, nature: {
dottedGreenRoots: 18
, greenRoots: 13
, limeRoots: 6
, goldRoots: 3
, stripedGoldRoots: 1
, crystalRoots: v => parseInt(v / 2) + 1
, stripedCrystalRoots: v => parseInt(v / 4) + 1
}
, metallic: {
bronzeBar: 1000
, ironBar: 700
, silverBar: 500
, goldBar: 300
, promethiumBar: 25
, runiteBar: 1
}
, energy: {
shrimp: 50
, sardine: 20
, tuna: 4
, swordfish: 1
, shark: v => parseInt(v / 3) + 1
, whale: v => parseInt(v / 6) + 1
}
, orb: {
blue: 3
, green: 1
, red: v => parseInt(v / 3) + 1
}
, gem: {
sapphire: 8
, emerald: 3
, ruby: 1
, diamond: v => parseInt(v / 5) + 1
}
};
const essenceObserver = new Map();
function essenceCellStyle(cell, fulfilled)
{
cell.style.backgroundColor = fulfilled ? '' : 'red';
cell.style.color = fulfilled ? '' : 'white';
}
function essenceSetFulfilled(key, value, el)
{
const fulfilled = window[key] >= value;
essenceCellStyle(el.previousElementSibling, fulfilled);
essenceCellStyle(el, fulfilled);
essenceCellStyle(el.nextElementSibling, fulfilled);
}
function essenceRequirements(amount, type)
{
if (essenceObserver.has(type))
{
essenceObserver.get(type).forEach((fn, key) =>
{
unobserve(key, fn);
});
}
const observerMap = new Map();
const makeString = 'make' + type[0].toUpperCase() + type.substr(1) + 'Essence';
for (let key in essenceMultiplier[type])
{
const elId = makeString + key[0].toUpperCase() + key.substr(1) + '-needed';
const el = document.getElementById(elId);
const mult = essenceMultiplier[type][key];
const value = amount == 0 ? 0 : (typeof mult === 'function' ? mult(amount) : amount * mult);
el.textContent = formatNumber(value);
const windowKey = type == 'orb' ? 'empty' + key[0].toUpperCase() + key.substr(1) + 'Orb' : key;
essenceSetFulfilled(windowKey, value, el);
const observeFn = observe(windowKey, () => essenceSetFulfilled(windowKey, value, el));
observerMap.set(windowKey, observeFn);
}
essenceObserver.set(type, observerMap);
}
function fixMagic()
{
// move roots to magic panel
const parent = document.getElementById('magic-tab');
const roots = document.querySelectorAll('[id$="Roots-box"]');
for (let i = 0; i < roots.length; i++)
{
const el = roots[i].parentNode;
el.setAttribute('tooltip', el.getAttribute('tooltip').replace(/ \(Used in the magic skill\)$/, ''));
parent.appendChild(el);
}
const style = document.createElement('style');
style.innerHTML = `
#magic-tab .inventory-item-box-farming
{
float: left;
}
`;
document.head.appendChild(style);
// improve tooltip of spell book
const magicBook = document.getElementById('item-magicBook-box').parentNode;
function updateTooltip()
{
const pages = [];
for (let i = 1; i <= 6; i++)
{
if (window['bindedMagicPage' + i] == '1')
{
pages.push(i);
}
}
const pagesString = pages.length === 0 ? '-' : pages.join(', ');
magicBook.setAttribute('tooltip', `Spell Book (binded pages: ${pagesString})`);
}
updateTooltip();
observe([
'bindedMagicPage1'
, 'bindedMagicPage2'
, 'bindedMagicPage3'
, 'bindedMagicPage4'
, 'bindedMagicPage5'
, 'bindedMagicPage6'
], () => updateTooltip());
const oldEmptyEssenceDialogue2 = window.emptyEssenceDialogue2;
window.emptyEssenceDialogue2 = (type) =>
{
oldEmptyEssenceDialogue2(type);
if (type == 'nature')
{
const input = document.querySelector(
'#emptyEssence2-dialog-nature table.table-stats tr:last-child > td:last-child input'
);
if (input && input.style.display != 'none')
{
input.style.display = 'none';
const inputCell = input.parentNode;
inputCell.style.border = 0;
const neededCell = inputCell.previousElementSibling;
neededCell.style.border = 0;
const imgCell = neededCell.previousElementSibling;
imgCell.style.border = 0;
}
}
};
window.refreshOresValuesWhenMakingEssences = (amount) =>
{
essenceRequirements(amount, 'mineral');
};
window.refreshOilValuesWhenMakingEssences = (amount) =>
{
essenceRequirements(amount, 'oil');
};
window.refreshSeedsValuesWhenMakingEssences = (amount) =>
{
essenceRequirements(amount, 'nature');
};
window.refreshBarValuesWhenMakingEssences = (amount) =>
{
essenceRequirements(amount, 'metallic');
};
window.refreshFoodValuesWhenMakingEssences = (amount) =>
{
essenceRequirements(amount, 'energy');
};
window.refreshOrbValuesWhenMakingEssences = (amount) =>
{
essenceRequirements(amount, 'orb');
};
window.refreshGemValuesWhenMakingEssences = (amount) =>
{
essenceRequirements(amount, 'gem');
};
}
/**
* fix number format
*/
function formatNumber(num)
{
// return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return parseFloat(num).toLocaleString('en');
}
function fixNumberFormat()
{
// fix achievements
const achievementFixes = {
'achABiggerWall': 'Sell excactly ' + formatNumber(1e7) + ' stone to the shop.'
, 'ach1000Potions': 'Drink a total of ' + formatNumber(1e3) + ' potions. '
, 'achMaxInt': 'Have a total of ' + formatNumber(Math.pow(2, 31)-1) + ' ores in your inventory. '
, 'achSmelter': 'Smelt a total of ' + formatNumber(5e5) + ' bars. '
};
for (let id in achievementFixes)
{
const row = document.getElementById(id);
row.cells[1].firstChild.textContent = achievementFixes[id];
}
const oldLoadAchievements = window.loadAchievements;
window.loadAchievements = () =>
{
oldLoadAchievements();
document.getElementById('total-potions-drank').innerHTML = formatNumber(window.totalPotionsDrank) + ' potions drank';
document.getElementById('total-bars-smelted').innerHTML = formatNumber(window.totalBarsSmelted) + ' bars smelted';
document.getElementById('statVendor2').innerHTML = formatNumber(window.statVendor);
document.getElementById('total-spellsCasted-ach').innerHTML = formatNumber(window.spellsCasted);
};
function checkAchievementRow(row)
{
if (window[row.id] != 1)
{
return false;
}
const pinkSpan = row.cells[1].children[0];
if (pinkSpan)
{
pinkSpan.style.color = 'blue';
}
return true;
}
const table = document.querySelector('#ach-tab table.table-stats');
const rows = table.rows;
for (let i = 0; i < rows.length; i++)
{
const row = rows[i];
if (row.id && !checkAchievementRow(row))
{
observe(row.id, () => checkAchievementRow(row));
}
}
// fix explorers energy
const oldExplorerTick = window.explorerTick;
window.explorerTick = () =>
{
oldExplorerTick();
const energyElement = document.getElementById('energy-amount');
energyElement.innerHTML = 'Energy: ' + formatNumber(window.energy);
};
// fix leveling up in crafting
window.setConvertBarToXpOnKeyDown = (amount) =>
{
var starDustCounter = window.bindedUpgradeEnchantedHammer >= 1 ? 10 : 13;
document.getElementById('enchantedHammer-XP-hint-box').style.display = 'block';
document.getElementById('enchantedHammer-XP-earned').innerHTML = '+'
+ formatNumber(getXPEarnedWhenConvertingBar(barToConvertToXpType) * amount.value);
document.getElementById('enchantedHammer-XP-total-stardust-cost').innerHTML = '-'
+ formatNumber(getXPEarnedWhenConvertingBar(barToConvertToXpType) * amount.value * starDustCounter);
};
// fix blue coins
const oldLoadCoins = window.loadCoins;
window.loadCoins = () =>
{
oldLoadCoins();
document.getElementById('platinumCoinsAmount-statusbar').innerHTML = formatNumber(window.platinumCoins);
};
// fix xp in "select a seed" dialog
const seedButtons = document.querySelectorAll('#seed-menu-popup > div[id^="btn-"]');
for (let i = 0; i < seedButtons.length; i++)
{
const cells = seedButtons[i].children[0].rows[0].cells;
const xpNode = cells[cells.length-1].lastChild;
const text = xpNode.textContent.replace(/(\d)\D+(?=\d)/g, '$1');
const xp = parseInt(text, 10);
xpNode.textContent = text.replace(xp.toString(10), formatNumber(xp));
}
// fix oil
const oldLoadMiscVariables = window.loadMiscVariables;
window.loadMiscVariables = () =>
{
oldLoadMiscVariables();
document.getElementById('span-oilPerSecond').innerHTML = formatNumber(window.oilPerSeconds);
document.getElementById('span-oilLosePerSecond').innerHTML = '-' + formatNumber(oilLosePerSeconds);
};
// fix artifact star dust calculation
const artifactInput = document.getElementById('amount-to-convert-artifact');
artifactInput.onkeyup = function ()
{
const xpRate = document.getElementById('artifact-xp-rate').value;
const sdRate = window.bindedExploringOrb == 1 ? 22 : 26;
document.getElementById('artifact-xp-earned').innerHTML = formatNumber(xpRate * this.value);
document.getElementById('artifact-stardust-needed').innerHTML = formatNumber(xpRate * this.value * 22);
};
const oldOpenArtifactDialogue = window.openArtifactDialogue;
window.openArtifactDialogue = (artifact, xp) =>
{
oldOpenArtifactDialogue(artifact, xp);
artifactInput.onkeyup(null);
};
}
/**
* initialize notifications
*/
function observeTimer(k, onComplete, zero = 0)
{
observe(k, (key, oldValue, newValue) =>
{
if (oldValue > zero && newValue == zero)
{
onComplete(key);
}
});
}
function notifyCoop(msg)
{
window.send('OPEN_TAB=COOP');
return notify('Group Task', {
body: msg.replace(/!$/, '.')
, icon: 'images/icons/coop.png'
}).then((n) =>
{
n.onclick = () =>
{
window.focus();
window.openTab('coop');
n.close();
};
return n;
});
}
function notifyVendor()
{
/*
"I have changed my items, come check them out."<br><br><img src="images/shop/vendor.png" width="120px" height="140px">
*/
return notify('Vendor', {
body: 'The vendor changed his items.'
, icon: 'images/shop/vendor.png'
})
.then((n) =>
{
n.onclick = () =>
{
window.focus();
window.openTab('vendor');
n.close();
};
return n;
})
;
}
function notifyBoat(msg)
{
/*
<b>Your boat brings back:</b><br><br><span class="exploring-norm-loot"><img class="small-img" src="images/exploring/rawSardine.png"> 2</span> <span class="exploring-norm-loot"><img class="small-img" src="images/exploring/rawTuna.png"> 1</span>
*/
const tmp = document.createElement('templateWrapper');
tmp.innerHTML = msg;
const loot = [];
const lootEls = tmp.querySelectorAll('.exploring-norm-loot');
for (let i = 0; i < lootEls.length; i++)
{
const el = lootEls[i];
const num = parseInt(el.textContent, 10);
const match = el.innerHTML.match(/\/([^\/]+)\.png"/);
if (match)
{
const itemName = match[1].replace(/([a-z])([A-Z])/, (wholeMatch, m1, m2) =>
{
return m1 + ' ' + m2.toLowerCase();
});
loot.push(num + ' ' + itemName);
}
else
{
loot.push(el.innerHTML);
}
}
return notify('Fishing boat returns', {
body: 'Your boat brings back: ' + loot.join(', ')
, icon: 'images/exploring/fishingBoat.png'
})
.then((n) =>
{
n.onclick = () =>
{
window.focus();
window.openTab('archaeology');
n.close();
};
return n;
})
;
}
function notifyAchievement()
{
/*
You have completed an achievement
*/
return notify('Achievement got', {
body: 'You have completed an achievement.'
, icon: 'images/shop/ach.png'
})
.then((n) =>
{
n.onclick = () =>
{
window.focus();
window.openTab('ach');
n.close();
};
return n;
})
;
}
function notifyMsg(msg)
{
if (msg === 'You have completed your group task!' ||
/ has completed his group task\.$/.test(msg))
{
return notifyCoop(msg);
}
else if (/I have changed my items, come check them out/.test(msg))
{
return notifyVendor();
}
else if (/Your boat brings back:/.test(msg))
{
notifyBoat(msg);
}
else if (msg === 'You have completed an achievement')
{
notifyAchievement();
}
else if (document.hidden || !document.hasFocus())
{
/*
Your account has been running for 234 Minutes
*/
notify('Message from server', {
body: msg
// , icon: 'images/minerals/diamond.png'
}).then((n) =>
{
n.onclick = () =>
{
window.focus();
n.close();
};
});
}
return Promise.reject();
}
function initNotifications()
{
if (!getSetting('showNotifications'))
{
return;
}
notify = (title, options) =>
{
if (!("Notification" in window) ||
Notification.permission === 'denied')
{
console.info('notification:', title, options);
return Promise.reject('Notification permission denied');
}
if (Notification.permission === 'granted')
{
return Promise.resolve(new Notification(title, options));
}
return Notification.requestPermission().then(() => notify(title, options));
};
Notification.requestPermission().then(function (result)
{
if (result == 'denied')
{
console.error('Permission to show notifications has been denied by the user.');
}
});
// don't send TAB_OFF
window.checkIfTabIsOpen = () =>
{
if (window.tabOn == 0)
{
window.send('TAB_ON');
window.tabOn = 1;
}
};
function addClickListener(n, tabName)
{
n.onclick = () =>
{
window.focus();
window.openTab(tabName);
n.close();
};
return n;
}
let lastFarmingNotification;
observeTimer(['farmingPatchTimer1', 'farmingPatchTimer2', 'farmingPatchTimer3', 'farmingPatchTimer4', 'farmingPatchTimer5', 'farmingPatchTimer6'], (key) =>
{
const now = (new Date).getTime();
const timeDiff = now - (lastFarmingNotification || 0);
if (timeDiff < 10e3)
{
return;
}
lastFarmingNotification = now;
notify('Harvest', {
body: 'One or more of your crops is ready for harvest.'
, icon: 'images/icons/watering-can.png'
}).then((n) => addClickListener(n, 'farming'));
}, 1);
observeTimer('exploringTimer', (key) =>
{
notify('Explorer ready', {
body: 'Your explorer is back.'
, icon: 'images/icons/archaeology.png'
}).then((n) => addClickListener(n, 'archaeology'));
});
observeTimer('furnaceCurrentTimer', (key) =>
{
notify('Furnace ready', {
body: 'Your smelting has finished.'
, icon: 'images/crafting/' + furnaceLevels[window.bindedFurnaceLevel] + 'Furnace.gif'
}).then((n) => addClickListener(n, 'repair'));
});
observeTimer('rocketTimer', (key) =>
{
notify('Rocket ready', {
body: 'You landed on the moon.'
, icon: 'images/crafting/rocket.png'
}).then((n) => addClickListener(n, 'repair'));
});
observeTimer('robotTimer', (key) =>
{
notify('Robot ready', {
body: 'Your robot is back.'
, icon: 'images/crafting/robot.png'
}).then((n) => addClickListener(n, 'repair'));
});
observeTimer('fishingBoatTimer', (key) =>
{
notify('Fishing boat ready', {
body: 'Your fishing boat is back.'
, icon: 'images/exploring/fishingBoat.png'
}).then((n) => addClickListener(n, 'archaeology'));
});
observeTimer('largeFishingBoatTimer', (key) => (key) =>
{
notify('Large fishing boat ready', {
body: 'Your large fishing boat is back.'
, icon: 'images/exploring/largeFishingBoat.png'
}).then((n) => addClickListener(n, 'archaeology'));
});
/*
// potions
'starDustPotionTimer'
'coinPotionTimer'
'seedPotionTimer'
'smeltingPotionTimer'
'oilPotionTimer'
'miningPotionTimer'
'superStarDustPotionTimer'
'fastFurnacePotionTimer'
'superCompostPotionTimer'
'megaStarDustPotionTimer'
'superOilPotionTimer'
'whaleFishingPotionTimer'
'fishingPotionTimer'
'essencePotionTimer'
'megaOilPotionTimer'
'superEssencePotionTimer'
'sparklingCompostPotionTimer'
'engineeringPotionTimer'
// magic effects
'superDrillsTimer'
'superGemFinderTimer'
'smallSipsTimer'
'superPirateTimer'
'superCrushersTimer'
'superGiantDrillsTimer'
'fastVendorTimer'
'superRoadHeadersTimer'
'animatedAxeTimer'
'superExcavatorsTimer'
// ?
'compostTimer'
'eatingTimer'
'exploringTimeReductionPerc'
'ghostEssenceTimer'
*/
}
/**
* fix level bar
*/
function fixLevelBar()
{
// size changing: 1267x65 -> 1256x105
document.getElementById('level-status-up').style.lineHeight = '102px';
const style = document.createElement('style');
style.innerHTML = `
tr[id^="level-status-row"] > td > img:first-child[width="40px"]
{
margin: 0 5px;
}
#level-status-row2 > td > img:first-child
{
height: 50px;
}
.unlock-skill-btn
{
margin-left: 5px;
}
span[id^="progress-percentage-"][id$="-small"]
{
height: calc(90% + 2px);
margin: 0;
}
#fishingBoat-timer > img:first-child
{
width: 53px;
height: 40px;
}
`;
document.head.appendChild(style);
}
/**
* fix message box
*/
function fixMsgBox()
{
const oldDialogFn = window.$.fn.dialog;
window.$.fn.dialog = function (...args)
{
if (args[0] != 'close')
{
$('.ui-widget-header').show();
}
return oldDialogFn.apply(this, args);
};
const oldMessageBox = window.messageBox;
let timeout;
window.messageBox = (msg) =>
{
const $el = $('#dialog-timer');
if ($el.hasClass('ui-dialog-content'))
{
$el.dialog('destroy');
}
document.getElementById('dialog-text-timer').innerHTML = msg;
$el.dialog(
{
create: function (event, ui)
{
$('.ui-widget-header').hide();
}
, width: 550
, height: 100
, show:
{
effect: 'fade'
, duration: 50
}
, hide:
{
effect: 'fade'
, delay: 1000
, duration: 1000
}
}).dialog('close');
};
}
/**
* add a notification box (like the harvest one) for coop events
*/
function addCoopNotificationBox()
{
const notifBox = document.createElement('span');
notifBox.id = 'coop-notif';
notifBox.classList.add('notification-timer-box');
notifBox.style.width = 'auto';
notifBox.style.cursor = 'pointer';
notifBox.style.display = 'none';
notifBox.style.padding = '0 10px';
notifBox.onclick = () =>
{
window.openTab('coop');
window.send('OPEN_TAB=COOP');
};
notifBox.innerHTML = `<span class="activate-tooltip" title="Group task is finished">
<img width="46px" height="40px" style="vertical-align: middle; padding: 5px 0px 5px 0px;" src="images/icons/coop.png">
<span class="progress"></span>
</span>`;
document.getElementById('farming-notif').parentNode.appendChild(notifBox);
const coopProgress = notifBox.querySelector('span.progress');
const oldLoadCoop = window.loadCoop;
window.loadCoop = (data) =>
{
const dataArray = data == 'none' ? [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] : data.split('~');
const player = [
dataArray[0]
, dataArray[1]
, dataArray[2]
, dataArray[3]
];
const task_id = [
dataArray[4]
, dataArray[5]
, dataArray[6]
, dataArray[7]
];
const task_value = [
dataArray[8]
, dataArray[9]
, dataArray[10]
, dataArray[11]
];
const task_neededValue = [
dataArray[12]
, dataArray[13]
, dataArray[14]
, dataArray[15]
];
function isPlayer(i)
{
return player[i] !== 'none' && player[i] !== 'claimed';
}
const started = task_id.every((id, i) => !isPlayer(i) || id != 0);
const totalNum = player.filter((name, i) => isPlayer(i)).length;
const finishedNum = task_value.filter((value, i) => isPlayer(i) && value == task_neededValue[i]).length;
const showBox = started && finishedNum > 0;
notifBox.style.display = showBox ? '' : 'none';
coopProgress.textContent = finishedNum == totalNum ? '' : finishedNum + '/' + totalNum;
const i = player.indexOf(window.username);
const thisFinished = started && task_value[i] == task_neededValue[i];
coopProgress.style.color = thisFinished ? 'lime' : '';
for (let j = 1; j <= 4; j++)
{
const row = document.getElementById('started-row-p' + j);
if (row)
{
row.style.backgroundColor = j == (i+1) ? 'lightblue' : '';
}
}
return oldLoadCoop(data);
};
window.send('OPEN_TAB=COOP');
}
/**
* fix chat
*/
const chatHistoryKey = 'chatHistory';
const maxChatHistoryLength = 100;
const reloadedChatData = {
timestamp: 0
, username: ''
, userlevel: 0
, sigil: 0
, tag: 0
, type: -1
, msg: '[...]'
};
let chatHistory = [];
function add2ChatHistory(data)
{
const splitArray = data.split('~');
data = {
timestamp: (new Date()).getTime()
, username: splitArray[0]
, userlevel: parseInt(splitArray[1], 10)
, sigil: parseInt(splitArray[3], 10)
, tag: parseInt(splitArray[2], 10)
, type: parseInt(splitArray[5], 10)
, msg: splitArray[4]
};
if (data.type == 2)
{
data.userlevel = window.getGlobalLevel();
}
chatHistory.push(data);
chatHistory = chatHistory.slice(-maxChatHistoryLength);
localStorage.setItem(chatHistoryKey, JSON.stringify(chatHistory));
return data;
}
function getChatTab(username)
{
const chatTabs = document.getElementById('chat-tabs');
let tab = chatTabs.querySelector('div.chat-tab[data-username="' + username + '"]');
if (!tab)
{
tab = document.createElement('div');
tab.className = 'chat-tab';
tab.dataset.username = username;
tab.dataset.new = 0;
const filler = chatTabs.querySelector('.filler');
if (filler)
{
chatTabs.insertBefore(tab, filler);
}
else
{
chatTabs.appendChild(tab);
}
}
return tab;
}
function getChatDiv(username)
{
const id = 'chat-' + (username == '' ? 'area-div' : 'pm-' + username);
let div = document.getElementById(id);
if (!div)
{
div = document.createElement('div');
div.setAttribute('disabled', 'disabled');
div.id = 'chat-pm-' + username;
div.className = 'chat-area-div';
const height = document.getElementById('chat-area-div').style.height;
div.style.height = height;
const generalChat = document.getElementById('chat-area-div');
generalChat.parentNode.insertBefore(div, generalChat);
}
return div;
}
function changeChatTab(oldTab, newTab)
{
const oldChatDiv = getChatDiv(oldTab.dataset.username);
oldChatDiv.classList.remove('selected');
const newChatDiv = getChatDiv(newTab.dataset.username);
newChatDiv.classList.add('selected');
const toUsername = newTab.dataset.username;
const newTextPlaceholder = toUsername == '' ? window.username + ':' : 'PM to ' + toUsername + ':';
document.getElementById('textbox-chat').placeholder = newTextPlaceholder;
if (window.isAutoScrolling)
{
setTimeout(() => newChatDiv.scrollTop = newChatDiv.scrollHeight);
}
}
const chatSigils = [
null
, { key: 'maxLevel', title: 'Maxed Skills' }
, { key: 'maxMining', title: 'Master in Mining' }
, { key: 'maxCrafting', title: 'Master in Crafting' }
, { key: 'maxBrewing', title: 'Master in Brewing' }
, { key: 'maxFarming', title: 'Master in Farming' }
, { key: 'hardcore', title: 'Hardcore Account' }
, { key: 'halloween2015', title: 'Halloween 2015' }
, { key: 'maxExploring', title: 'Master in Exploring' }
, { key: 'christmas2015', title: 'Chirstmas 2015' }
, { key: 'maxMagic', title: 'Master in Magic' }
, { key: 'easter2016', title: 'Holiday' }
, { key: 'coop', title: 'COOP' }
, { key: 'maxCooking', title: 'Master in Cooking' }
, { key: 'halloween2016', title: 'Halloween 2016' }
, { key: 'christmas2016', title: 'Chirstmas 2016' }
];
const chatTags = [
null
, { key: 'donor', name: '' }
, { key: 'contributor', name: 'Contributor' }
, null
, { key: 'mod', name: 'Moderator' }
, { key: 'dev', name: 'Dev' }
];
const linkParseRegex = /(^|\s)(https?:\/\/\S+|\S*www\.|\S+\.(?:com|ca|co|net|us))(\s|$)/;
function isPM(data)
{
return data.type == 1 || data.type == 2;
}
const locale = 'en-US';
const localeOptions = {
hour12: false
, year: 'numeric'
, month: 'long'
, day: 'numeric'
, hour: '2-digit'
, minute: '2-digit'
, second: '2-digit'
};
function newRefreshChat(data)
{
// username is 3-12 characters long
let chatbox = document.getElementById('chat-area-div');
if (mutedPeople.some((name) => name == data.username))
{
return;
}
const isThisPm = isPM(data);
const msgUsername = data.type == 2 ? window.username : data.username;
const historyIndex = chatHistory.indexOf(data);
const dataBefore = historyIndex != -1 &&
chatHistory.slice(0, historyIndex).reverse().find((d) =>
{
return isThisPm && isPM(d) || !isThisPm && !isPM(d);
})
;
let isSameUser = false;
let isSameTime = false;
if (dataBefore)
{
const beforeUsername = dataBefore.type == 2 ? window.username : dataBefore.username;
isSameUser = beforeUsername === msgUsername;
isSameTime = Math.floor(data.timestamp / 1000 / 60) - Math.floor(dataBefore.timestamp / 1000 / 60) === 0;
}
const d = new Date(data.timestamp);
const hour = (d.getHours() < 10 ? '0' : '') + d.getHours();
const minute = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
const sigil = chatSigils[data.sigil] || { key: '', title: '' };
const tag = chatTags[data.tag] || { key: '', name: '' };
const formattedMsg = data.msg.replace(new RegExp(linkParseRegex, 'g'), (wholeMatch, before, link, after) =>
{
if (/%22|%27|%3E|%3C|>|<|;|~|\\"|<|>|javascript:|window|document|cookie/.test(link))
{
return wholeMatch;
}
link = (link.startsWith('http') ? '' : 'http://') + link;
return before + `<a href="${link}" target="_blank">${link}</a>` + after;
});
const msgTitle = data.type == -1 ? 'Chat loaded on ' + d.toLocaleString(locale, localeOptions) : '';
let chatSegment = `<span class="chat-msg" data-type="${data.type}" data-tag="${tag.key}">`
+ `<span
class="timestamp"
data-hour="${hour}"
data-minute="${minute}"
title="${d.toLocaleString(locale, localeOptions)}"
data-same-time="${isSameTime}"></span>`
+ `<span class="user" data-name="${data.username}" data-same-user="${isSameUser}">`
+ `<span class="sigil ${sigil.key}" title="${sigil.title}"></span>`
+ `<span class="tag chat-tag-${tag.key}">${tag.name}</span>`
+ `<span
class="name"
data-level="${data.userlevel}"
oncontextmenu="searchPlayerHicores('${msgUsername}');return false;"
onclick="preparePM('${msgUsername}')">${msgUsername}</span>`
+ `</span>`
+ `<span class="msg" title="${msgTitle}">${formattedMsg}</span>`
+ `</span>`;
const chatTab = getChatTab(isThisPm ? data.username : '');
if (!chatTab.classList.contains('selected'))
{
chatTab.dataset.new = parseInt(chatTab.dataset.new, 10) + 1;
}
if (isThisPm)
{
window.lastPMFrom = data.username;
chatbox = getChatDiv(data.username);
}
const tmp = document.createElement('templateWrapper');
tmp.innerHTML = chatSegment;
while (tmp.childNodes.length > 0)
{
chatbox.appendChild(tmp.childNodes[0]);
}
if (window.isAutoScrolling)
{
setTimeout(() => chatbox.scrollTop = chatbox.scrollHeight);
}
}
function applyChatStyle()
{
const style = document.createElement('style');
style.innerHTML = `
span.chat-msg
{
display: flex;
margin-bottom: 1px;
}
.chat-msg .timestamp::before
{
color: hsla(0, 0%, 50%, 1);
font-size: .9rem;
}
.chat-msg .timestamp[data-same-time="true"]::before
{
}
#chat-toggleTimestamps:checked ~ div[id^="chat-"] .chat-msg:not([data-type="-1"]) .timestamp::before
{
content: attr(data-hour) ':' attr(data-minute);
display: inline-block;
margin: 0 5px;
width: 2.5rem;
}
.chat-msg[data-type="1"] { color: purple; }
.chat-msg[data-type="2"] { color: purple; }
.chat-msg[data-type="3"] { color: blue; }
.chat-msg[data-tag="contributor"] { color: green; }
.chat-msg[data-tag="mod"] { color: #669999; }
.chat-msg[data-tag="dev"] { color: #666600; }
.chat-msg .user
{
margin-right: 5px;
white-space: nowrap;
}
.chat-msg .user[data-same-user="true"]:not([data-name="none"])
{
opacity: .3;
}
.chat-msg .user .name:not([data-level=""])::after
{
content: ' (' attr(data-level) '):';
}
.chat-msg .user .sigil:not([class$=" "])::before
{
background-size: 20px 20px;
content: '';
display: inline-block;
margin-right: 1px;
width: 20px;
height: 20px;
vertical-align: middle;
}
.chat-msg .user .sigil.maxLevel::before { background-image: url('images/icons/stats.png'); }
.chat-msg .user .sigil.maxCrafting::before { background-image: url('images/icons/anvil.png'); }
.chat-msg .user .sigil.maxMining::before { background-image: url('images/icons/pickaxe.png'); }
.chat-msg .user .sigil.maxBrewing::before { background-image: url('images/brewing/vialofwater_chat.png'); }
.chat-msg .user .sigil.maxFarming::before { background-image: url('images/icons/watering-can.png'); }
.chat-msg .user .sigil.maxExploring::before { background-image: url('images/icons/archaeology.png'); }
.chat-msg .user .sigil.maxCooking::before { background-image: url('images/icons/cookingskill.png'); }
.chat-msg .user .sigil.maxMagic::before { background-image: url('images/magic/wizardHatIcon.png'); }
.chat-msg .user .sigil.hardcore::before { background-image: url('images/icons/hardcoreIcon.png'); }
.chat-msg .user .sigil.coop::before { background-image: url('images/icons/groupTaskBadge5.png'); }
.chat-msg .user .sigil.halloween2015::before { background-image: url('images/icons/halloween2015.png'); }
.chat-msg .user .sigil.christmas2015::before { background-image: url('images/sigils/christmas2015.png'); }
.chat-msg .user .sigil.easter2016::before { background-image: url('images/sigils/easter2016.png'); }
.chat-msg .user .sigil.halloween2016::before { background-image: url('images/sigils/halloween2016.png'); }
.chat-msg .user .sigil.christmas2016::before { background-image: url('images/sigils/christmas2016.png'); }
.chat-msg .user .tag
{
margin-right: 3px;
}
.chat-msg .user .tag.chat-tag-
{
display: none;
}
.chat-msg .user .tag.chat-tag-donor::before
{
background-image: url('images/icons/donor-icon.gif');
background-size: 20px 20px;
content: '';
display: inline-block;
height: 20px;
width: 20px;
vertical-align: middle;
}
.chat-msg .user .name
{
color: rgba(0, 0, 0, 0.7);
}
.chat-msg[data-type="-1"] .user > *,
.chat-msg[data-type="1"] .user > .sigil,
.chat-msg[data-type="1"] .user > .tag,
.chat-msg[data-type="2"] .user > .sigil,
.chat-msg[data-type="2"] .user > .tag,
.chat-msg[data-type="3"] .user > *
{
display: none;
}
.chat-msg[data-type="3"] .user::before
{
background: -webkit-linear-gradient(#004747, #00FFFF);
background: -o-linear-gradient(#004747, #00FFFF);
background: -moz-linear-gradient(#004747, #00FFFF);
background: linear-gradient(#004747, #00FFFF);
border: 1px solid black;
color: white;
content: 'Server Message';
font-family: Comic Sans MS, "Times New Roman", Georgia, Serif;
font-size: 9pt;
padding: 0px 5px 2px 5px;
}
.chat-msg .msg
{
word-wrap: break-word;
min-width: 0;
}
#chat-box-area .chat-area-div
{
width: 100%;
height: 130px;
display: none;
}
#chat-box-area .chat-area-div.selected
{
display: block;
}
#chat-tabs
{
background-color: hsla(0, 0%, 90%, 1);
display: flex;
margin: 10px -10px -10px;
flex-wrap: wrap;
}
#chat-tabs .chat-tab
{
background-color: gray;
border-top: 1px solid black;
border-right: 1px solid black;
cursor: pointer;
display: inline-block;
font-weight: normal;
padding: 0.3rem .6rem;
}
#chat-tabs .chat-tab.selected
{
background-color: silver;
border-top-color: silver;
}
#chat-tabs .chat-tab.filler
{
background-color: transparent;
border-right: 0;
box-shadow: inset 5px 5px 5px -5px rgba(0, 0, 0, 0.5);
color: transparent;
cursor: default;
flex-grow: 1;
}
#chat-tabs .chat-tab::before
{
content: attr(data-username);
}
#chat-tabs .chat-tab:not(.filler)[data-username=""]::before
{
content: 'Server';
}
#chat-tabs .chat-tab::after
{
content: '(' attr(data-new) ')';
font-size: .9rem;
font-weight: bold;
margin-left: .4rem;
}
#chat-tabs .chat-tab[data-new="0"]::after
{
font-weight: normal;
}
`;
document.head.appendChild(style);
}
function fixChat()
{
if (!getSetting('useNewChat'))
{
return;
}
const chatBoxArea = document.getElementById('chat-box-area');
const toggles = chatBoxArea.querySelectorAll('input[value^="Toggle"]');
for (let i = 0; i < toggles.length; i++)
{
const toggle = toggles[i];
const parent = toggle.parentNode;
const toggleWhat = toggle.value.replace('Toggle ', '');
const id = 'chat-toggle' + toggleWhat;
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = id;
checkbox.checked = ['Autoscroll', 'Timestamps'].includes(toggleWhat);
parent.insertBefore(checkbox, toggle);
const label = document.createElement('label');
label.htmlFor = id;
label.textContent = toggleWhat;
parent.insertBefore(label, toggle);
toggle.style.display = 'none';
checkbox.addEventListener('change', () =>
{
if (toggleWhat == 'Autoscroll')
{
window.isAutoScrolling = !window.isAutoScrolling;
}
else if (toggleWhat == 'Timestamps')
{
window.showTimestamps = !window.showTimestamps;
}
else
{
toggle.click();
}
});
}
// add chat tabs
const chatTabs = document.createElement('div');
chatTabs.id = 'chat-tabs';
chatTabs.addEventListener('click', (event) =>
{
const newTab = event.target;
if (!newTab.classList.contains('chat-tab') || newTab.classList.contains('filler'))
{
return;
}
const oldTab = chatTabs.querySelector('.chat-tab.selected');
if (newTab == oldTab)
{
return;
}
oldTab.classList.remove('selected');
newTab.classList.add('selected');
newTab.dataset.new = 0;
changeChatTab(oldTab, newTab);
});
chatBoxArea.appendChild(chatTabs);
const generalTab = getChatTab('');
generalTab.classList.add('selected');
const generalChatDiv = getChatDiv('');
generalChatDiv.classList.add('selected');
// works only if username length of 1 isn't allowed
const fillerTab = getChatTab('f');
fillerTab.classList.add('filler');
const oldSendChat = window.sendChat;
window.sendChat = (msg) =>
{
const selectedTab = document.querySelector('.chat-tab.selected');
if (selectedTab.dataset.username != '')
{
msg = '/pm ' + selectedTab.dataset.username + ' ' + msg;
}
oldSendChat(msg);
};
const oldChatBoxZoom = window.chatBoxZoom;
window.chatBoxZoom = (zoom) =>
{
oldChatBoxZoom(zoom);
const height = document.getElementById('chat-area-div').style.height;
const chatDivs = chatBoxArea.querySelectorAll('div[id^="chat-pm-"]');
for (let i = 0; i < chatDivs.length; i++)
{
chatDivs[i].style.height = height;
}
};
chatHistory = JSON.parse(localStorage.getItem(chatHistoryKey) || JSON.stringify(chatHistory));
const lastNotPM = chatHistory.slice(0).reverse().find((d) =>
{
return d.type != 1 && d.type != 2;
});
if (lastNotPM && lastNotPM.type != -1)
{
reloadedChatData.timestamp = (new Date()).getTime();
chatHistory.push(reloadedChatData);
}
chatHistory.forEach(d => newRefreshChat(d));
// reset the new counter for all tabs
const tabs = document.querySelectorAll('.chat-tab');
for (let i = 0; i < tabs.length; i++)
{
tabs[i].dataset.new = 0;
}
applyChatStyle();
const oldRefreshChat = window.refreshChat;
window.refreshChat = (data) =>
{
data = add2ChatHistory(data);
return newRefreshChat(data);
};
}
/**
* fix crafting
*/
function fixCrafting()
{
// show selection for the bar type
const oldSetConvertBarToXpAgain = window.setConvertBarToXpAgain;
window.setConvertBarToXpAgain = (barType, amount) =>
{
oldSetConvertBarToXpAgain(barType, amount);
const selector = (bar = '') => `#enchanted-hammer-boxes input[type="image"][src$="${bar}bar.png"]`;
const barImages = document.querySelectorAll(selector());
for (let i = 0; i < barImages.length; i++)
{
barImages[i].style.backgroundColor = '';
}
const img = document.querySelector(selector(barType));
img.style.backgroundColor = 'red';
};
}
/**
* activity log
*/
const activityLogKey = 'activityLog';
const maxActivityLogLength = 200;
let activityLog = [];
function add2ActivityLog(cmd)
{
const split = cmd.split('=');
const data = {
type: split[0]
, msg: split.slice(1).join('=')
, read: false
};
activityLog.push(data);
activityLog = activityLog.slice(-maxActivityLogLength);
localStorage.setItem(activityLogKey, JSON.stringify(activityLog));
return data;
}
function updateActivity(data)
{
const activityLogEl = document.getElementById('activity-log');
data.read = activityLogEl.classList.contains('open');
}
function initActivityLog()
{
activityLog = JSON.parse(localStorage.getItem(activityLogKey) || JSON.stringify(activityLog));
activityLog.forEach(d => updateActivity(d));
}
/**
* init
*/
function init()
{
console.info('[%s] "DHO Fixed" up and running!', (new Date).toLocaleTimeString());
initObservable();
initSettings();
initActivityLog();
initNotifications();
fixKeyItems();
fixFarming();
fixServerMsg();
applyNewItemStyle();
applyNewKeyItemStyle();
expandEquipment();
highlightRequirements();
fixMarket();
improveLevelCalculation();
fixInventory();
fixMachinery();
fixBrewing();
fixTabs();
hideCraftingRecipes();
hideEquipment();
improveDialogBtns();
hideMaxRecipes();
fixMagic();
fixNumberFormat();
fixLevelBar();
fixMsgBox();
fixChat();
addCoopNotificationBox();
fixCrafting();
}
document.addEventListener('DOMContentLoaded', () =>
{
const oldLoadCommand = window.loadCommand;
const msgPrefix = 'MESSAGE=';
const msgBoxPrefix = 'MSG_BOX=';
window.loadCommand = (cmd) =>
{
if (!fullyLoaded && cmd.startsWith('ITEMS_DATA='))
{
const ret = oldLoadCommand(cmd);
fullyLoaded = true;
init();
return ret;
}
if (!/^(?:ITEMS_DATA|SET_TRADABLE_ITEMS|REFRESH_OFFER_TRADE|REFRESH_CHAT|PLAY_SOUND|COOP|MESSAGE|MSG_BOX)=/.test(cmd))
{
console.debug('new cmd:', cmd);
}
// add some activity log here!
if (/^(?:QUESTION|MESSAGE|MSG_BOX)=/.test(cmd))
{
const data = add2ActivityLog(cmd);
updateActivity(data);
}
if (cmd === msgBoxPrefix + 'You have completed an achievement')
{
const msg = cmd.substr(msgBoxPrefix.length);
notifyMsg(msg)
.catch(() => oldLoadCommand(cmd))
;
return;
}
else if (cmd.startsWith(msgPrefix))
{
const msg = cmd.substr(msgPrefix.length);
notifyMsg(msg)
.catch(() => oldLoadCommand(cmd))
;
return;
}
return oldLoadCommand(cmd);
};
});
})();