Diamond Hunt QoL

Quality of Life changes for DiamondHunt Online

目前為 2016-01-08 提交的版本,檢視 最新版本

// ==UserScript==
// @name		Diamond Hunt QoL
// @namespace	Diamond Hunt QoL
// @version		1.0
// @description	Quality of Life changes for DiamondHunt Online
// @author		John / WhoIsYou
// @match		*.diamondhunt.co/game.php
// @run-at document-end
// @grant		unsafeWindow
// ==/UserScript==

/** 
FEATURES:
- Clickable links in chat! Very simple regex pattern that won't catch ALL links, but it will catch the majority / common formats
- Market exclusion list, to filter out users you don't like or don't want to see (such as justanoob)
- Market clutter remover, only shows the 5 first/cheapest listings for any single, item not counting your own items
- Market inclusion list, doesn't count any offers by these users in the 5 offer limit per item (always shows these people's items)
- Rocket timer that's fairly accurate (though RNG based) based on average distances traveled per second found via logging
- Furnace timer (accurate) in addition to the percentage. Bar type text removed and iron furnace image replaced with current item being smelted
- Improved timers for rocket, robot, furnace, exploring, potions, farming (only if you have the donor perk) in the HH:MM:SS format - Magic timers coming soon™
- Farming, Furnace, and Exploring timers factor in the donor perks if you have them, giving much more accurate timers than the default (though farming is still slightly RNG based)
- Added indicators that let you know when you have Explorer's Potions or Artifact Potions active
**/

// Array of potions that use timers
var timedPotionList = ["starDustPotion", "seedPotion", "smeltingPotion", "oilPotion", "miningPotion", "superStarDustPotion", 
		"coinPotion", "compost", "fishingPotion", "essencePotion", "fastFurnacePotion", "superOilPotion", "megaStarDustPotion", "whaleFishingPotion", "megaOilPotion"];

// Array of spells that use timers
var timedSpellList = [];

// Users to be excluded from the market listing
var marketExclusionList = ["justanoob"];

// Users to always be included in the market listing, useful if you have a habit of trading with a specific person
var marketInclusionList = [];

// Users to be included in the market listing

// Run this once on page load
function runOnce() {
	createNotificationBoxes();
}

// Do the stuff in our runOnce() function
runOnce();

/** Quality of Life Changes, mostly timer additions or changes, and chatbox changes **/

// Makes some changes to the "loadOffers()" function to filter out unwanted market items from the market table
// Filters out any users in the marketExclusionList, and filters out all but the cheapest 5 offers for any item not counting your own
unsafeWindow.loadOffers = function(unparsedData)
{
	var lastItem = "";
	var lastItemCount = "";

	dataArray = unparsedData.split("~");
	var table = document.getElementById("market-buy-table");
	resetGEBox(1);
	resetGEBox(2);
	resetGEBox(3);
	table.innerHTML = "<tr class='table-th'><th>Player</th><th>Item</th><th>Icon</th><th>Amount</th><th>Price each</th></tr>"
	for(var i = 0; i < dataArray.length; i++) {
		var playerId = dataArray[i]; i++;
		var playerUsername = dataArray[i]; i++;
		var item = dataArray[i];i++;
		var total = dataArray[i];i++;
		var pricePer = dataArray[i];i++;
		var collect = dataArray[i];i++;
		var slot = dataArray[i];i++;
		var isPlatinum = dataArray[i];i++;
		var category = dataArray[i];

		// Loads the items we're selling into our collection boxes
		if(playerUsername == username) {
			loadGEBoxSelling(item + "~" + total + "~" + pricePer + "~" + collect, slot);
		}
		
		// Adds offers to the market table as long as they're not in the excluded users list
		// Keeps tracks of how many offers exist for each individual type of item and only displays the first (cheapest) five not counting our own items
		if(total > 0 && marketExclusionList.indexOf(playerUsername) < 0) {
			if (unsafeWindow.username != playerUsername && marketInclusionList.indexOf(playerUsername) < 0) {
				if (item == lastItem) {
					if (lastItemCount >= 5) {
						continue
					} else {
						lastItemCount += 1;
					}
				} else {
					lastItem = item;
					lastItemCount = 1;
				}
			}
			// Adds any items that made it through the filter to the market table
			applyToBuyingTable(playerId, playerUsername, item, total, pricePer, slot, isPlatinum, category);
		}
	}
}

// Returns the given number of seconds formated as HH:MM:SS
function formatTime(seconds) {
	var hours = Math.floor(seconds / 3600);
	var mins = Math.floor((seconds % 3600) / 60);
	var secs = Math.round(seconds % 60);
	hourString = ("00" + hours.toString()).slice(-2) + ":";
	minString = ("00" + mins.toString()).slice(-2) + ":";
	secString = ("00" + secs.toString()).slice(-2);
	return hourString +  minString + secString
}

/*	Rocket info (found from logging) that we need to know
	Rocket lifts off at a speed of 0-10km/s (averaging 5km/s) until there is about 383401km left in the trip 
	The majority of the trip (~383100-~383125) is traveled at 25-75km/s (averaging 50km/s)
	At ~275-300km remaining the rocket slows down to 0-10km/s (averagig 5km/s)
	At ~30km remaining the rocket slows down to a constant 1km/s
*/
// Roughly calculate the time remaining until the rocket journey completes. Uses averages gathered so it may vary slightly.
function getRocketTimer() {
	var totalDistanceRemaining = unsafeWindow.rocketTimer;
	var liftoffDistanceRemaining = Math.max((totalDistanceRemaining-383401), 0);
	var mainTravelDistanceRemaining = Math.max((totalDistanceRemaining-liftoffDistanceRemaining-300), 0);
	var endSlowdownDistanceRemaining = (totalDistanceRemaining > 300) ? 300-30 : Math.max((totalDistanceRemaining-30), 0);
	var landingDistanceRemaining = (totalDistanceRemaining > 30) ? 30 : totalDistanceRemaining;

	var averageSecondsRemaining = Math.ceil(liftoffDistanceRemaining/5) + Math.ceil(mainTravelDistanceRemaining/50) + Math.ceil(endSlowdownDistanceRemaining/5) + landingDistanceRemaining;

	return formatTime(averageSecondsRemaining)
}

// Make changes to the document to replace our rocket timer, giving us an estimate time until completion based on average times to travel each kilometer (depending on the distance remaining)
function replaceRocketTimer() {
	document.getElementById("notification-timer-span-rocket").innerHTML = getRocketTimer();
}

// Calculates the time left until your furnace is done smelting the current load and formats the time into a string
// Takes the donor furnace booster perk into account but not the magic spell
function getfurnaceTimer() {
	var ftt = (unsafeWindow.furnaceTimer > 0) ? Math.round(unsafeWindow.furnaceTotalTimer * 0.75) : unsafeWindow.furnaceTotalTimer // Donor perk check
	var timeRemaining = ftt - Math.max(0, unsafeWindow.furnaceCurrentTimer);
	return formatTime(timeRemaining)
}

// Returns an image path based on the "furnaceBarId" variable (item you're currently smelting)
function getProductImageByFurnaceId(id) {
	switch (unsafeWindow.furnaceBarId) {
		case "1": 
			return "minerals/bronzeBar.png"
		case "2":
			return "minerals/ironBar.png"
		case "3":
			return "minerals/silverBar.png"
		case "4":
			return "minerals/goldBar.png"
		case "5":
			return "minerals/glass.png"
		case "6":
			return "minerals/promethiumBar.png"
		default:
			return "crafting/ironfurnace.gif"
	}
}

// Make changes to the document to replace our furnace timer with a new image and a time rather than just a percentage
function replaceFurnaceTimer() {
	var doc = document.getElementById("furnace-timer").outerHTML;
	var match = doc.match(/images\/(.*)\"/).pop();
	doc = doc.replace(match, getProductImageByFurnaceId(unsafeWindow.furnaceBarId));
	document.getElementById("furnace-timer").outerHTML = doc;
	document.getElementById("notification-timer-span-furnace").innerHTML = getfurnaceTimer() + " (" + unsafeWindow.furnacePerc + "%)";
}

// Loops through all potions which can have a timer (timedPotionList) and returns an array with timer variable name of those which are currently active
function getActivePotionTimers() {
	var activePotions = [];
	for (var i = 0; i < timedPotionList.length; i++) {
		var potionTimer = timedPotionList[i]+"Timer";
		if (unsafeWindow[potionTimer] > 0) {
			activePotions.push(timedPotionList[i]); 
		}
	}
	return activePotions
}

// Replaces the timers for all active potions with new timers formated as HH:MM:SS
function replaceActivePotionTimers(activePotions) {
	for (var i = 0; i < activePotions.length; i++) {
		var activePotion = activePotions[i];
		document.getElementById("notification-timer-span-"+activePotion).innerHTML = formatTime(unsafeWindow[activePotion+"Timer"]);
	}
}

// Creates two new notification boxes, one for explorer's potions, and one for artifact potions. These are initially hidden until you have one active.
function createNotificationBoxes() {
	// A little bit of voodoo to get the correct element to edit since it has no ID (smitty pls add ID).
	// This is prone to breaking fairly easily if any center tags are added or removed in the wrong order, but it's not a difficult fix.
	var mainGameElement = document.getElementById("main-game");
	if (mainGameElement) {
		var centerElements = mainGameElement.getElementsByTagName("center");
		if (centerElements && centerElements.length > 1) {
			var notificationElement = centerElements[1].getElementsByTagName("div");
			if (notificationElement && notificationElement.length > 0) {
				var doc = notificationElement[0];
				if (doc.innerHTML.indexOf("<!-- timer box -->")) {
					// Backslashes allow you to break lines in JS, neat thing to keep in mind
					doc.innerHTML += '\
									<!-- (DH QOL)  Explorer\'s Potion Notification -->\
									<span class="notification-timer-box-potion" id="explorersPotion-notification-box" style="display: none;">\
									<img width="30px" height="40px" style="vertical-align: middle;padding:5px 0px 5px 0px;" src="images/brewing/explorersPotion.png" title="Explorer\'s Potion">\
									<span id="notification-span-explorersPotion"></span>\
									</span>\
									<!-- (DH QOL)  End Notification -->\
									\
									<!-- (DH QOL)  Artifact Potion Notification -->\
									<span class="notification-timer-box-potion" id="artifactPotion-notification-box" style="display: none;">\
									<img width="30px" height="40px" style="vertical-align: middle;padding:5px 0px 5px 0px;" src="images/brewing/artifactPotion.png" title="Artifact Potion">\
									<span id="notification-span-artifactPotion"></span>\
									</span>\
									<!-- (DH QOL) End Notification -->\
									';
				}
			}
		}
	}
}

// Show or hide our custom potion notifications depending on whether or not those potions are active
function updateCustomPotionNotification() {
	var explorerNotificationElement = document.getElementById("explorersPotion-notification-box");
	var artifactNotificationElement = document.getElementById("artifactPotion-notification-box");
	if (explorerNotificationElement) {
		if (unsafeWindow.explorersPotionOn > 0) {
			document.getElementById("notification-span-explorersPotion").innerHTML = unsafeWindow.explorersPotionOn + " Active";
			explorerNotificationElement.style.display = "inline-block";
		} else {
			explorerNotificationElement.style.display = "none";
		}
	}
	if (artifactNotificationElement) {
		if (unsafeWindow.artifactPotionIsActivated > 0) {
			document.getElementById("notification-span-artifactPotion").innerHTML = unsafeWindow.artifactPotionIsActivated + " Active";
			artifactNotificationElement.style.display = "inline-block";
		} else {
			artifactNotificationElement.style.display = "none";
		}
	}
}

// Get & format our robot timer
function getRobotTimer() {
	var robotTime = unsafeWindow.robotTimer;
	var mins = Math.floor(robotTime / 60);
	var secs = Math.round(robotTime % 60);
	minString = ("00" + mins.toString()).slice(-2) + ":";
	secString = ("00" + secs.toString()).slice(-2);
	return minString + secString
}

// Make changes to the document to replace our robot timer to be formatted in minutes and seconds rather than just seconds
function replaceRobotTimer() {
	document.getElementById("notification-timer-span-robot").innerHTML = "Returns in " + getRobotTimer();
}

// Get & format our exploring timer, takes donor perk into account
function getExploringTimer() {
	var exploringTime = (unsafeWindow.explorerTimer > 0) ? Math.round(unsafeWindow.exploringTimer * 0.8) : unsafeWindow.exploringTimer;
	return formatTime(exploringTime)
}

// Make changes to the document to replace our exploring timer to be formatted in HH:MM:SS rather than the default
function replaceExploringTimer() {
	document.getElementById("notification-timer-span-exploring").innerHTML = getExploringTimer();
}

// Get & format our magic cooldown timer
function getMagicCooldown() {
	var mcd = unsafeWindow.magicCoolDown;
	return formatTime(mcd)
}

// Make changes to the document to replace our magic cooldown timer to be formatted in HH:MM:SS rather than the default
function replaceMagicCooldownTimer() {
	document.getElementById("notification-timer-span-magicCoolDown").innerHTML = getMagicCooldown();
}

// Replaces diamondhunt's "loadMiscVariabled()" function with a new one that updates our many timers to be more fancy
var oldLoadMiscVariables = unsafeWindow.loadMiscVariables;
unsafeWindow.loadMiscVariables = function() {
	oldLoadMiscVariables();
	replaceRocketTimer();
	replaceFurnaceTimer();
	replaceRobotTimer();
	replaceExploringTimer();
	replaceMagicCooldownTimer();
}

// Replaces diamondhunt's "loadPotionTimers()" function with a new one that updates our potion timers to be more fancy
// Also adds a notification for explorer's potions and artifact potions (#updateCustomePotionNotifications())
var oldLoadPotionTimers = unsafeWindow.loadPotionTimers;
unsafeWindow.loadPotionTimers = function() {
	oldLoadPotionTimers();
	replaceActivePotionTimers(getActivePotionTimers());
	updateCustomPotionNotification()
}

// Replace the farming timers with new timers formated in HH:MM:SS, also more accurately estimates the time if you have the Gardener donor perk
// Farming has slight RNG to it though, so it may not tick correctly every second
function replaceFarmingTimers() {
	var patchIDs = ["1", "2", "3", "4"];
	(unsafeWindow.hasFarmingPatch5) ? patchIDs.push("5") : patchIDs;
	(unsafeWindow.hasFarmingPatch6) ? patchIDs.push("6") : patchIDs;
	for (var i = 0; i < patchIDs.length; i++) {
		var matches = document.getElementById("farming-patch-status-"+patchIDs[i]).innerHTML.match(/>(.* min remaining)/);
		if (matches != null) {
			var match = matches.pop();
			var timeRemaining = unsafeWindow.gardenerTimer == "0" ? unsafeWindow["farmingPatchTimer"+patchIDs[i]] : Math.round(unsafeWindow["farmingPatchTimer"+patchIDs[i]] * 0.8);
			document.getElementById("farming-patch-status-"+patchIDs[i]).innerHTML = document.getElementById("farming-patch-status-"+patchIDs[i]).innerHTML.replace(match, formatTime(timeRemaining));
		}
	}
}

// Replaces diamondhunt's "refreshFarmingPatch()" function with a new one that updates our farming timers to be more fancy
var oldRefreshFarmingPatch = unsafeWindow.refreshFarmingPatch;
unsafeWindow.refreshFarmingPatch = function(patchId, seedId, seedName, deadCropImg) {
	oldRefreshFarmingPatch(patchId, seedId, seedName, deadCropImg);
	replaceFarmingTimers();
}

// Make links in chat clickable, not 100% perfect, but better than nothing
var oldRefreshChat = unsafeWindow.refreshChat;
unsafeWindow.refreshChat = function(data) {
	var pattern = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
	data = data.replace(pattern, "<a href='$1' target='_blank'>$1</a>");
	oldRefreshChat(data);
}

/** End of Quality of Life Changes **/

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址