// ==UserScript==
// @name DHO Fixed
// @namespace FileFace
// @description Improve Diamond Hunt Online and fix some inconsistencies
// @version 0.24.2
// @author Zorbing
// @grant none
// @run-at document-start
// @include http://www.diamondhunt.co/game.php
// ==/UserScript==
/**
* TODO:
* - add repair option to machine dialog
* - change style of vendor items like the other items
* - highlight requirements for select a seed when clicking a farm patch
* - highlight requirements in spellbook
*/
(function ()
{
'use strict';
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 (!observedKeys.has(key))
{
observedKeys.set(key, []);
}
observedKeys.get(key).push(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;
};
}
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
}
, 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
}
};
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()
{
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)';
}
});
}
/**
* global constants
*/
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'];
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);
}
const seedOrder = ['bloodLeafSeeds', 'dottedGreenLeafSeeds', 'redMushroomSeeds', 'potatoSeeds', 'greenLeafSeeds', 'strawberrySeeds', 'redMushroomTreeSeeds', 'blewitMushroomSeeds', 'wheatSeeds', 'starDustSeeds', 'blewitMushroomTreeSeeds', 'snapeGrassSeeds', 'limeLeafSeeds', 'appleTreeSeeds', 'iceBerrySeeds', 'goldLeafSeeds', 'starDustTreeSeeds', 'stripedLeafSeeds', 'essenceSeeds', 'crystalLeafSeeds', 'megaDottedGreenLeafSeeds', 'megaRedMushroomSeeds', 'megaGreenLeafSeeds', 'essenceTreeSeeds', 'megaBlewitMushroomSeeds', 'megaLimeLeafSeeds', 'stripedCrystalLeafSeeds'];
const seedTitle = {
potatoSeeds: 'Potato Seeds'
, strawberrySeeds: 'Strawberry Seeds'
, wheatSeeds: 'Wheat Seeds'
, dottedGreenLeafSeeds: 'Dotted Green Leaf Seeds'
, greenLeafSeeds: 'Green Leaf Seeds'
, limeLeafSeeds: 'Lime Leaf Seeds'
, goldLeafSeeds: 'Gold Leaf Seeds'
, stripedLeafSeeds: 'Striped Leaf Seeds'
, crystalLeafSeeds: 'Crystal Leaf Seeds'
, redMushroomSeeds: 'Red Mushroom Seeds'
, blewitMushroomSeeds: 'Blewit Mushroom Seeds'
, appleTreeSeeds: 'Apple Tree Seeds'
, snapeGrassSeeds: 'Snape Grass Seeds'
, redMushroomTreeSeeds: 'Red Mushroom Tree Seeds'
, blewitMushroomTreeSeeds: 'Blewit Mushroom Tree Seeds'
, starDustSeeds: 'Star Dust Seeds'
};
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-', '');
input.title = seedTitle[key];
}
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;
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;
}
}
}
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);
});
}
const bgColor = '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': 'achFishermen'
};
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 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 (!window.hasOwnProperty(imgKey) && !imgSrc2Key.hasOwnProperty(imgKey))
{
console.debug('missing key handling:', key);
}
keys2Observe.push(key);
}
valueSpan.style.color = fulfilled ? '' : 'red';
everythingFulfilled = everythingFulfilled && (isSkill || fulfilled);
}
levelEl.style.backgroundColor = everythingFulfilled ? '' : bgColor;
reqEl.style.backgroundColor = everythingFulfilled ? '' : bgColor;
row.style.backgroundColor = everythingFulfilled ? 'rgb(194, 255, 133)' : 'rgb(255, 128, 128)';
if (init)
{
for (let key of keys2Observe)
{
observe(key, (key, oldValue, newValue) => 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'
}
};
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)
{
continue;
}
checkRequirements(row, xpName, true);
}
}
}
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();
};
}
const maxLevel = 100;
const maxLevelVirtual = 1000;
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;
}
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);
}
}
}
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'
};
function getMachineCount(machine)
{
return window['binded' + machine[0].toUpperCase() + machine.substr(1)];
}
function getOilValueFromMachine(machine)
{
return (oilConsumption[machine] || 0) * getMachineCount(machine);
}
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 + '%';
}
// 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, ',');
};
if (!getSetting('improveMachineryDialog'))
{
return;
}
window.getOilValueFromMachine = getOilValueFromMachine;
window.openOilDialogue = openOilDialogue;
}
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 smallImgItems = ['vial','enchantedVial','pureWaterPotion','starDustPotion','seedPotion','smeltingPotion','oilPotion','miningPotion','superStarDustPotion','coinPotion','explorersPotion','compost','magicCoolDownPotion','engineeringPotion','starGemPotion','essencePotion','superEssencePotion','superStarGemPotion','megaStarGemPotion','fastFurnacePotion','artifactPotion','superCompostPotion','sparklingCompostPotion','superOilPotion','goldenStriperPotion','crystalStriperPotion','chestPotion','megaStarDustPotion','megaOilPotion','superChestPotion','fishingPotion','whaleFishingPotion'];
for (let item of smallImgItems)
{
document.querySelector('#item-' + item + '-box img.item-box-img').style.margin = marginFix;
}
}
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;
}
}
}
const recipes = {
shovel: 'shovel'
, promethiumWrench: 'bindedPromethiumWrench'
, glassBlowingPipe: 'bindedGlassBlowingPipe'
, oilPipe: 'bindedOilPipe'
, planter: 'bindedPlanter'
, trowel: 'bindedTrowel'
, brewingKit: 'brewingKitBinded'
, rocket: 'bindedRocket'
, redPumpJack: 'bindedRedPumpJack'
, explorersBrush: 'bindedExplorersBrush'
, oilFactory: 'bindedOilFactory'
, robot: 'bindedRobot'
, oilRefinery: 'bindedOilRefinery'
, superRobot: 'bindedSuperRobot'
};
function hideCraftingRecipes()
{
if (!getSetting('hideSomeCraftRecipes'))
{
return;
}
const bindedFurnaceLevel = parseInt(window.bindedFurnaceLevel, 10);
for (let i = 1; i <= bindedFurnaceLevel; i++)
{
const row = document.getElementById('craft-' + furnaceLevels[i] + 'Furnace');
if (row)
{
row.style.display = 'none';
}
}
for (let key in recipes)
{
if (window[recipes[key]] != 0)
{
document.getElementById('craft-' + key).style.display = 'none';
}
}
// exploring - crafting
let bindedOvenLevel = -1;
for (let i = ovenLevels.length-1; i >= 0; i--)
{
const type = ovenLevels[i];
if (bindedOvenLevel == -1 && window['binded' + type[0].toUpperCase() + type.substr(1) + 'Oven'] > 0)
{
bindedOvenLevel = i;
}
const row = document.getElementById('craft-' + type + 'Oven');
if (row && bindedOvenLevel >= i)
{
row.style.display = 'none';
}
}
const equipmentTypes = {
'weapon': 'Sword'
, 'helmet': 'Helmet'
, 'body': 'Body'
, 'leg': 'Legs'
};
for (let key in equipmentTypes)
{
const type = equipmentTypes[key];
let highestLevel = parseInt(window[key + 'SlotId'], 10) - 1;
for (let i = barTypes.length-1; i >= 0; i--)
{
const bar = barTypes[i];
if (highestLevel < i && window[bar + type] > 0)
{
highestLevel = i;
}
const row = document.getElementById('craft-' + bar + type);
if (row && highestLevel >= i)
{
row.style.display = 'none';
}
}
}
// magic - crafting
const magicRodTypes = ['gold', 'promethium', 'runite', 'dragon'];
let bindedWandLevel = -1;
let bindedStaffLevel = -1;
for (let i = magicRodTypes.length-1; i >= 0; i--)
{
const type = magicRodTypes[i];
// check wand recipes
if (bindedWandLevel == -1 && window['binded' + type[0].toUpperCase() + type.substr(1) + 'Wand'] > 0)
{
bindedWandLevel = i;
}
const wandRow = document.getElementById('craft-' + type + 'Wand');
if (wandRow && bindedWandLevel >= i)
{
wandRow.style.display = 'none';
}
// check staff recipes
if (bindedStaffLevel == -1 && window['binded' + type[0].toUpperCase() + type.substr(1) + 'Staff'] > 0)
{
bindedStaffLevel = i;
}
const staffRow = document.getElementById('craft-' + type + 'Staff');
if (staffRow && bindedStaffLevel >= i)
{
staffRow.style.display = 'none';
}
}
}
function hideEquipment()
{
if (!getSetting('hideEquipment'))
{
return;
}
const types = {
'sword': 'weapon'
, 'helmet': 'helmet'
, 'body': 'body'
, 'legs': 'leg'
};
const levels = ['bronze', 'iron', 'silver', 'gold', 'promethium', 'runite', 'dragon'];
for (let type in types)
{
const typeName = type[0].toUpperCase() + type.substr(1);
const currentLevel = parseInt(window[types[type] + 'SlotId'], 10);
// hide not more than gold equipment
for (let i = 0; i < currentLevel && i < 4; i++)
{
const el = document.getElementById('item-' + levels[i] + typeName + '-box');
if (el)
{
el.parentNode.style.display = 'none';
}
}
}
}
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 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';
}
};
}
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);
}
}
function applyNewItemStyle()
{
if (!getSetting('applyNewItemStyle'))
{
return;
}
// change how the items are styled
const style = document.createElement('style');
style.innerHTML = `
span[class^="inventory-item-box"]
{
position: relative;
}
span[class^="inventory-item-box"] img.item-box-img
{
position: absolute;
margin: 0 !important;
}
img.item-box-img[height="30px"] { top: 52.5px; }
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="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; }
span[class^="inventory-item-box"] span[id$="mount"],
#ancientCrystalChargesSpan
{
background-color: black;
border-top: 1px solid rgba(255, 255, 255, 0.5);
color: white !important;
font-weight: normal;
margin: 0 !important;
padding: 3px;
padding-right: 9px;
text-align: center;
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
span[class^="inventory-item-box"] span[id$="mount"]:not(#energy-amount)::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"]
{
font-weight: normal;
padding-left: 20px;
position: relative;
top: 1px;
}
span[class^="inventory-item-box"] span[id$="-price"]::before
{
content: '';
display: inline-block;
width: 20px;
height: 20px;
position: absolute;
left: 0;
background-image: url('images/pic_coin.png');
background-size: 20px 20px;
}
span[class^="inventory-item-box"] img[src="images/pic_coin.png"]
{
display: none;
}
`;
document.head.appendChild(style);
// remove line breaks
const brs = document.querySelectorAll('[class^="inventory-item-box"] 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');
}
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);
}
}
}
/**
* init
*/
document.addEventListener('DOMContentLoaded', function ()
{
initObservable();
initSettings();
fixKeyItems();
fixFarming();
fixServerMsg();
applyNewItemStyle();
applyNewKeyItemStyle();
expandEquipment();
highlightRequirements();
fixMarket();
improveLevelCalculation();
fixInventory();
fixMachinery();
fixBrewing();
fixTabs();
hideCraftingRecipes();
hideEquipment();
improveDialogBtns();
});
})();