- // ==UserScript==
- // @name DH2 Fixed
- // @namespace FileFace
- // @description Improve Diamond Hunt 2
- // @version 0.28.1
- // @author Zorbing
- // @grant none
- // @run-at document-start
- // @include http://www.diamondhunt.co/game.php
- // ==/UserScript==
-
- (function ()
- {
- 'use strict';
-
-
-
- /**
- * observer
- */
-
- 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 updateValue(key, newValue)
- {
- if (window[key] === newValue)
- {
- return false;
- }
-
- const oldValue = window[key];
- window[key] = newValue;
- (observedKeys.get(key) || []).forEach(fn => fn(key, oldValue, newValue));
- return true;
- }
-
-
-
- /**
- * global constants
- */
-
- const tierLevels = ['empty', 'sapphire', 'emerald', 'ruby', 'diamond'];
- const furnaceLevels = ['stone', 'bronze', 'iron', 'silver', 'gold'];
- const furnaceCapacity = [10, 30, 75, 150, 300];
- const ovenLevels = ['bronze', 'iron', 'silver', 'gold'];
- const maxOilStorageLevel = 3; // 7
- const oilStorageSize = [10e3, 50e3, 100e3];
-
-
-
- /**
- * general functions
- */
-
- let styleElement = null;
- function addStyle(styleCode)
- {
- if (styleElement === null)
- {
- styleElement = document.createElement('style');
- document.head.appendChild(styleElement);
- }
- styleElement.innerHTML += styleCode;
- }
- function getBoundKey(key)
- {
- return 'bound' + key[0].toUpperCase() + key.substr(1);
- }
- function getTierKey(key, tierLevel)
- {
- return tierLevels[tierLevel] + key[0].toUpperCase() + key.substr(1);
- }
- function formatNumber(num)
- {
- return parseFloat(num).toLocaleString('en');
- }
- function formatNumbersInText(text)
- {
- return text.replace(/\d(?:[\d',\.]*\d)?/g, (numStr) =>
- {
- return formatNumber(parseInt(numStr.replace(/\D/g, ''), 10));
- });
- }
- function now()
- {
- return (new Date()).getTime();
- }
- function padLeft(num, padChar)
- {
- return (num < 10 ? padChar : '') + num;
- }
- function formatTimer(timer)
- {
- timer = parseInt(timer, 10);
- const hours = Math.floor(timer / 3600);
- const minutes = Math.floor((timer % 3600) / 60);
- const seconds = timer % 60;
- return padLeft(hours, '0') + ':' + padLeft(minutes, '0') + ':' + padLeft(seconds, '0');
- }
-
-
-
- /**
- * hide crafting recipes of lower tiers or of maxed machines
- */
-
- function hideTierRecipes(max, getKey, init)
- {
- const keys2Observe = [];
- let maxLevel = 0;
- for (let i = max-1; i >= 0; i--)
- {
- const level = i+1;
- const key = getKey(i);
- const boundKey = getBoundKey(key);
- keys2Observe.push(key);
- keys2Observe.push(boundKey);
- if (window[key] > 0 || window[boundKey] > 0)
- {
- maxLevel = Math.max(maxLevel, level);
- }
-
- const recipeRow = document.getElementById('crafting-' + key);
- if (recipeRow)
- {
- const hide = level <= maxLevel;
- recipeRow.style.display = hide ? 'none' : '';
- }
- }
-
- if (init)
- {
- observe(keys2Observe, () => hideTierRecipes(max, getKey, false));
- }
- }
- function hideRecipe(key, max, init)
- {
- const maxValue = typeof max === 'function' ? max() : max;
- const boundKey = getBoundKey(key);
- const unbound = parseInt(window[key], 10);
- const bound = parseInt(window[boundKey], 10);
-
- const recipeRow = document.getElementById('crafting-' + key);
- if (recipeRow)
- {
- const hide = (bound + unbound) >= maxValue;
- recipeRow.style.display = hide ? 'none' : '';
- }
-
- if (init)
- {
- observe([key, boundKey], () => hideRecipe(key, max, false));
- }
- }
- function hideCraftedRecipes()
- {
- function processRecipes(init)
- {
- // furnace
- hideTierRecipes(
- furnaceLevels.length
- , i => furnaceLevels[i] + 'Furnace'
- , init
- );
- // oil storage
- hideTierRecipes(
- 7
- , i => 'oilStorage' + (i+1)
- , init
- );
- // oven recipes
- hideTierRecipes(
- ovenLevels.length
- , i => ovenLevels[i] + 'Oven'
- , init
- );
- // drills
- hideRecipe('drills', 10, init);
- // crushers
- hideRecipe('crushers', 10, init);
- // oil pipe
- hideRecipe('oilPipe', 1, init);
- // row boat
- hideRecipe('rowBoat', 1, init);
- }
- processRecipes(true);
-
- const oldProcessCraftingTab = window.processCraftingTab;
- window.processCraftingTab = () =>
- {
- const reinit = !!window.refreshLoadCraftingTable;
- oldProcessCraftingTab();
-
- if (reinit)
- {
- processRecipes(true);
- }
- };
- }
-
-
-
- /**
- * improve item boxes
- */
-
- function hideNumberInItemBox(key, setVisibility)
- {
- const itemBox = document.getElementById('item-box-' + key);
- const numberElement = itemBox.lastElementChild;
- if (setVisibility)
- {
- numberElement.style.visibility = 'hidden';
- }
- else
- {
- numberElement.style.display = 'none';
- }
- }
- function addSpan2ItemBox(key)
- {
- hideNumberInItemBox(key);
-
- const itemBox = document.getElementById('item-box-' + key);
- const span = document.createElement('span');
- itemBox.appendChild(span);
- return span;
- }
- function setOilPerSecond(span, oil)
- {
- span.innerHTML = `+ ${formatNumber(oil)} L/s <img src="images/oil.png" class="image-icon-20" style="margin-top: -2px;">`;
- }
- function improveItemBoxes()
- {
- // show capacity of furnace
- for (let i = 0; i < furnaceLevels.length; i++)
- {
- const key = furnaceLevels[i] + 'Furnace';
- const capacitySpan = addSpan2ItemBox(getBoundKey(key));
- capacitySpan.className = 'capacity';
- capacitySpan.textContent = 'Capacity: ' + formatNumber(furnaceCapacity[i]);
- }
-
- // show oil cap of oil storage
- for (let i = 0; i < maxOilStorageLevel; i++)
- {
- const key = 'oilStorage' + (i+1);
- const capSpan = addSpan2ItemBox(getBoundKey(key));
- capSpan.className = 'oil-cap';
- capSpan.textContent = 'Oil cap: ' + formatNumber(oilStorageSize[i]);
- }
-
- // show oil per second
- const handheldOilSpan = addSpan2ItemBox('handheldOilPump');
- setOilPerSecond(handheldOilSpan, 1*window.miner);
- observe('miner', () => setOilPerSecond(handheldOilSpan, 1*window.miner));
- const oilPipeSpan = addSpan2ItemBox('boundOilPipe');
- setOilPerSecond(oilPipeSpan, 50);
-
- // show current tier
- hideNumberInItemBox('emptyAnvil', true);
- hideNumberInItemBox('farmer', true);
- hideNumberInItemBox('planter', true);
- const tierItemList = ['pickaxe', 'shovel', 'hammer', 'axe', 'rake', 'fishingRod'];
- for (let tierItem of tierItemList)
- {
- for (let i = 0; i < tierLevels.length; i++)
- {
- const key = getTierKey(tierItem, i);
- const tierSpan = addSpan2ItemBox(tierItem == 'rake' ? key : getBoundKey(key));
- tierSpan.className = 'tier';
- tierSpan.textContent = 'Tier: ' + i;
- }
- }
-
- // show boat progress
- function setTransitText(span, isInTransit)
- {
- span.textContent = isInTransit ? 'In transit' : 'Ready';
- }
- const boatSpan = addSpan2ItemBox('boundRowBoat');
- setTransitText(boatSpan, window.rowBoatTimer > 0);
- observe('rowBoatTimer', () => setTransitText(boatSpan, window.rowBoatTimer > 0));
- const canoeSpan = addSpan2ItemBox('boundCanoe');
- setTransitText(canoeSpan, window.canoeTimer > 0);
- observe('canoeTimer', () => setTransitText(canoeSpan, window.canoeTimer > 0));
- }
-
-
-
- /**
- * fix wood cutting
- */
-
- function fixWoodcutting()
- {
- addStyle(`
- img.woodcutting-tree-img
- {
- border: 1px solid transparent;
- }
- `);
- }
-
-
-
- /**
- * fix chat
- */
-
- const lastMsg = new Map();
- function isMuted(user)
- {
- // return window.mutedPeople.some((name) => user.indexOf(name) > -1);
- return window.mutedPeople.includes(user);
- }
- function isSpam(user, msg)
- {
- return lastMsg.has(user) &&
- lastMsg.get(user).msg == msg &&
- // last message in the last 30 seconds?
- (now() - lastMsg.get(user).time) < 30e3;
- }
- function handleSpam(user, msg)
- {
- const msgObj = lastMsg.get(user);
- msgObj.time = now();
- msgObj.repeat++;
- // a user is allowed to repeat a message twice (write it 3 times in total)
- if (msgObj.repeat > 1)
- {
- window.mutedPeople.push(user);
- }
- }
- function fixChat()
- {
- const oldAddToChatBox = window.addToChatBox;
- window.addToChatBox = (userChatting, iconSet, tagSet, msg, isPM) =>
- {
- if (isMuted(userChatting))
- {
- return;
- }
- if (isSpam(userChatting, msg))
- {
- return handleSpam(userChatting, msg);
- }
- lastMsg.set(userChatting, {
- time: now()
- , msg: msg
- , repeat: 0
- });
-
- // add clickable links
- msg = msg.replace(/(https?:\/\/[^\s]+)/g, '<a target="_blank" href="$1">$1</a>');
-
- oldAddToChatBox(userChatting, iconSet, tagSet, msg, isPM);
- };
-
- // add checkbox instead of button for toggling auto scrolling
- const btn = document.querySelector('input[value="Toggle Autoscroll"]');
- const checkboxId = 'chat-toggle-autoscroll';
- // create checkbox
- const toggleCheckbox = document.createElement('input');
- toggleCheckbox.type = 'checkbox';
- toggleCheckbox.id = checkboxId;
- toggleCheckbox.checked = true;
- // create label
- const toggleLabel = document.createElement('label');
- toggleLabel.htmlFor = checkboxId;
- toggleLabel.textContent = 'Autoscroll';
- btn.parentNode.insertBefore(toggleCheckbox, btn);
- btn.parentNode.insertBefore(toggleLabel, btn);
- btn.style.display = 'none';
-
- toggleCheckbox.addEventListener('change', function ()
- {
- window.isAutoScrolling = this.checked;
- window.scrollText('none', this.checked ? 'lime' : 'red', (this.checked ? 'En' : 'Dis') + 'abled');
- });
- }
-
-
-
- /**
- * hopefully only temporary fixes
- */
-
- function temporaryFixes()
- {
- // fix recipe of oil storage 3
- const oldProcessCraftingTab = window.processCraftingTab;
- window.processCraftingTab = () =>
- {
- const reinit = !!window.refreshLoadCraftingTable;
- oldProcessCraftingTab();
-
- if (reinit)
- {
- // 200 instead of 100 gold bars
- window.craftingRecipes['oilStorage3'].recipeCost[2] = 200;
- document.getElementById('recipe-cost-oilStorage3-2').textContent = 200;
- window.showMateriesNeededAndLevelLabels('oilStorage3');
- }
- };
-
- // fix burn rate of ovens
- window.getOvenBurnRate = () =>
- {
- if (boundBronzeOven == 1)
- {
- return .5;
- }
- else if (boundIronOven == 1)
- {
- return .4;
- }
- else if (boundSilverOven == 1)
- {
- return .3;
- }
- else if (boundGoldOven == 1)
- {
- return .2;
- }
- return 1;
- };
-
- // fix grow time of some seeds
- const seeds = {
- 'limeLeafSeeds': '1 hour and 30 minutes'
- , 'greenLeafSeeds': '45 minutes'
- };
- for (let seedName in seeds)
- {
- const tooltip = document.getElementById('tooltip-' + seedName);
- tooltip.lastElementChild.lastChild.textContent = seeds[seedName];
- }
-
- // fix exhaustion timer
- const oldClientGameLoop = window.clientGameLoop;
- window.clientGameLoop = () =>
- {
- oldClientGameLoop();
- if (document.getElementById('tab-container-combat').style.display != 'none')
- {
- combatNotFightingTick();
- }
- };
-
- // fix scrollText (e.g. when joining the game and receiving xp at that moment)
- window.mouseX = window.innerWidth / 2;
- window.mouseY = window.innerHeight / 2;
- }
-
-
-
- /**
- * improve timer
- */
-
- function improveTimer()
- {
- window.formatTime = (seconds) =>
- {
- return formatTimer(seconds);
- };
- window.formatTimeShort2 = (seconds) =>
- {
- return formatTimer(seconds);
- };
-
- const barInfo = {
- 1: {
- name: 'bronze'
- , timePerBar: 1
- }
- , 2: {
- name: 'iron'
- , timePerBar: 5
- }
- , 3: {
- name: 'silver'
- , timePerBar: 10
- }
- , 4: {
- name: 'gold'
- , timePerBar: 30
- }
- , 5: {
- name: 'glass'
- , timePerBar: 1
- }
- };
- const smeltingPercEl = document.querySelector('span[data-item-display="smeltingPerc"]');
- const smeltingTimerEl = document.createElement('span');
- smeltingPercEl.style.display = 'none';
- smeltingPercEl.parentNode.lastElementChild.style.display = 'none';
- smeltingPercEl.parentNode.appendChild(smeltingTimerEl);
- let interval = null;
- let barName = '';
- let remainingTime = 0;
- function setRemainingTime()
- {
- smeltingTimerEl.textContent = formatTimer(Math.max(remainingTime, 0));
- }
- function updateSmeltingPerc()
- {
- const info = barInfo[window.smeltingBarType];
- if (info)
- {
- const totalTime = info.timePerBar * window.smeltingTotalAmount;
- const time = Math.round(totalTime * (1 - window.smeltingPerc / 100));
- if (interval == null || barName != info.name)
- {
- barName = info.name;
- remainingTime = time;
- interval = window.setInterval(() =>
- {
- remainingTime--;
- setRemainingTime();
- }, 1000);
- setRemainingTime();
- }
- // tolerate up to 2 seconds deviation (to make it smoother for the user)
- else if (Math.abs(remainingTime - time) > 2)
- {
- remainingTime = time;
- setRemainingTime();
- }
- }
- else if (interval)
- {
- window.clearInterval(interval);
- barName = '';
- interval = null;
- }
- }
- observe('smeltingPerc', () => updateSmeltingPerc());
- updateSmeltingPerc();
-
- // add tree grow timer
- const treeInfo = {
- 1: {
- name: 'Normal tree'
- // 3h = 10800s
- , growTime: 3 * 60 * 60
- }
- , 2: {
- name: 'Oak tree'
- // 6h = 21600s
- , growTime: 6 * 60 * 60
- }
- , 3: {
- name: 'Willow tree'
- // 8h = 28800s
- , growTime: 8 * 60 * 60
- }
- };
- function updateTreeInfo(place, nameEl, timerEl, init)
- {
- const idKey = 'treeId' + place;
- const growTimerKey = 'treeGrowTimer' + place;
- const lockedKey = 'treeUnlocked' + place;
-
- const info = treeInfo[window[idKey]];
- if (!info)
- {
- const isLocked = place > 4 && window[lockedKey] == 0;
- nameEl.textContent = isLocked ? 'Locked' : 'Nothing';
- timerEl.textContent = '';
- }
- else
- {
- nameEl.textContent = info.name;
- const remainingTime = info.growTime - parseInt(window[growTimerKey], 10);
- timerEl.textContent = remainingTime > 0 ? '(' + formatTimer(remainingTime) + ')' : 'Fully grown';
- }
-
- if (init)
- {
- observe(
- [idKey, growTimerKey, lockedKey]
- , () => updateTreeInfo(place, nameEl, timerEl, false)
- );
- }
- }
- for (let i = 0; i < 6; i++)
- {
- const treePlace = i+1;
- const treeContainer = document.getElementById('wc-div-tree-' + treePlace);
- treeContainer.style.position = 'relative';
- const infoEl = document.createElement('div');
- infoEl.setAttribute('style', 'position: absolute; top: 0; left: 0; right: 0; pointer-events: none; margin-top: 5px; color: white;');
- const treeName = document.createElement('div');
- treeName.style.fontSize = '1.2rem';
- infoEl.appendChild(treeName);
- const treeTimer = document.createElement('div');
- infoEl.appendChild(treeTimer);
- treeContainer.appendChild(infoEl);
-
- updateTreeInfo(treePlace, treeName, treeTimer, true);
- }
- }
-
-
-
- /**
- * improve smelting dialog
- */
-
- const smeltingRequirements = {
- 'glass': {
- sand: 1
- , oil: 10
- }
- , 'bronzeBar': {
- copper: 1
- , tin: 1
- , oil: 10
- }
- , 'ironBar': {
- iron: 1
- , oil: 100
- }
- , 'silverBar': {
- silver: 1
- , oil: 300
- }
- , 'goldBar': {
- gold: 1
- , oil: 1e3
- }
- };
- function improveSmelting()
- {
- const amountInput = document.getElementById('input-smelt-bars-amount');
- amountInput.type = 'number';
- amountInput.min = 0;
- amountInput.step = 5;
- function onValueChange(event)
- {
- smeltingValue = null;
- window.selectBar('', '', amountInput, document.getElementById('smelting-furnace-capacity').value);
- }
- amountInput.addEventListener('mouseup', onValueChange);
- amountInput.addEventListener('keyup', onValueChange);
- amountInput.setAttribute('onkeyup', '');
-
- const oldSelectBar = window.selectBar;
- let smeltingValue = null;
- window.selectBar = (bar, inputElement, inputBarsAmountEl, capacity) =>
- {
- const requirements = smeltingRequirements[bar];
- let maxAmount = capacity;
- for (let key in requirements)
- {
- maxAmount = Math.min(Math.floor(window[key] / requirements[key]), maxAmount);
- }
- if (amountInput.value > maxAmount)
- {
- smeltingValue = amountInput.value;
- amountInput.value = maxAmount;
- }
- else if (smeltingValue != null)
- {
- amountInput.value = Math.min(smeltingValue, maxAmount);
- if (smeltingValue <= maxAmount)
- {
- smeltingValue = null;
- }
- }
- return oldSelectBar(bar, inputElement, inputBarsAmountEl, capacity);
- };
-
- const oldOpenFurnaceDialogue = window.openFurnaceDialogue;
- window.openFurnaceDialogue = (furnace) =>
- {
- if (smeltingBarType == 0)
- {
- amountInput.max = getFurnaceCapacity(furnace);
- }
- return oldOpenFurnaceDialogue(furnace);
- };
- }
-
-
-
- /**
- * init
- */
-
- function init()
- {
- temporaryFixes();
-
- hideCraftedRecipes();
- improveItemBoxes();
- fixWoodcutting();
- fixChat();
- improveTimer();
- improveSmelting();
- }
- document.addEventListener('DOMContentLoaded', () =>
- {
- const oldDoCommand = window.doCommand;
- window.doCommand = (data) =>
- {
- if (data.startsWith('REFRESH_ITEMS='))
- {
- const itemDataValues = data.split('=')[1].split(';');
- const itemArray = [];
- for (var i = 0; i < itemDataValues.length; i++)
- {
- const [key, newValue] = itemDataValues[i].split('~');
- if (updateValue(key, newValue))
- {
- itemArray.push(key);
- }
- }
-
- window.refreshItemValues(itemArray, false);
-
- if (window.firstLoadGame)
- {
- window.loadInitial();
- window.firstLoadGame = false;
- init();
- }
- else
- {
- window.clientGameLoop();
- }
- return;
- }
- return oldDoCommand(data);
- };
- });
-
-
-
- /**
- * fix web socket errors
- */
-
- function webSocketLoaded(event)
- {
- if (window.webSocket == null)
- {
- console.error('no webSocket instance found!');
- return;
- }
-
- const messageQueue = [];
- const oldOnMessage = webSocket.onmessage;
- webSocket.onmessage = (event) => messageQueue.push(event);
- document.addEventListener('DOMContentLoaded', () =>
- {
- messageQueue.forEach(event => onMessage(event));
- webSocket.onmessage = oldOnMessage;
- });
-
- const commandQueue = [];
- const oldSendBytes = window.sendBytes;
- window.sendBytes = (command) => commandQueue.push(command);
- const oldOnOpen = webSocket.onopen;
- webSocket.onopen = (event) =>
- {
- window.sendBytes = oldSendBytes;
- commandQueue.forEach(command => window.sendBytes(command));
- return oldOnOpen(event);
- };
- }
- function isWebSocketScript(script)
- {
- return script.src.includes('socket.js');
- }
- function fixWebSocketScript()
- {
- if (!document.head)
- {
- return;
- }
-
- const scripts = document.head.querySelectorAll('script');
- let found = false;
- for (let i = 0; i < scripts.length; i++)
- {
- if (isWebSocketScript(scripts[i]))
- {
- // does this work?
- scripts[i].onload = webSocketLoaded;
- return;
- }
- }
-
- // create an observer instance
- const mutationObserver = new MutationObserver((mutationList) =>
- {
- mutationList.forEach((mutation) =>
- {
- if (mutation.addedNodes.length === 0)
- {
- return;
- }
-
- for (let i = 0; i < mutation.addedNodes.length; i++)
- {
- const node = mutation.addedNodes[i];
- if (node.tagName == 'SCRIPT' && isWebSocketScript(node))
- {
- mutationObserver.disconnect();
- node.onload = webSocketLoaded;
- return;
- }
- }
- });
- });
- mutationObserver.observe(document.head, {
- childList: true
- });
- }
- fixWebSocketScript();
- })();