Melvor Completion Log Helper

Replaces question mark icon in the Item Completion Log, Mastery Progression, Pet Completion and Monster Completion Log with greyed out images of items/pets/monsters that you have yet to get/kill and adds links to the wiki.

目前為 2020-09-14 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Melvor Completion Log Helper
// @namespace    http://tampermonkey.net/
// @version      0.3.4
// @description  Replaces question mark icon in the Item Completion Log, Mastery Progression, Pet Completion and Monster Completion Log with greyed out images of items/pets/monsters that you have yet to get/kill and adds links to the wiki.
// @author       Breindahl#2660
// @match        https://*.melvoridle.com/*
// @grant        none
// ==/UserScript==
/* jshint esversion: 6 */

// Made for version 0.16.3
// Big thanks to Visua#9999 for helping with parts of the code and troubleshooting

(function () {
	function injectScript(main) {
		var script = document.createElement('script');
		script.textContent = `try {(${main})();} catch (e) {console.log(e);}`;
		document.body.appendChild(script).parentNode.removeChild(script);
	}

	function script() {
		// Loading script
		console.log('Melvor Completion Log Helper Loaded');

			$('#modal-item-log').find('.block-options').prepend('<button id="toggleItems" class="btn btn-sm btn-success" style="margin:0px 10px; float:left;">Toggle Found</button>')

			$(document).ready(function(){
			  $("#toggleItems").click(function(){
				$(".item-found").toggle();
			  });
			});

			$('#modal-item-log').find('.block-options').prepend('<div class="input-group" style="float:left;width:auto"><input type="text" class="form-control text-danger btn btn-sm" id="searchTextboxItemLog" style="height:auto; text-align:left;" name="searchTextboxItemLog" placeholder="Search Item Log..." autocomplete="off"><div class="input-group-append"><button type="button" class="btn btn-danger btn-sm" id="clearItemLog">X</button></div></div>');

			const itemIds = items.map((_, id) => id);

			function updateItemLogSearch(search) {
				if (search === '') {
					$('#itemlog-container img').removeClass('d-none');
					return;
				}
				search = search.toLowerCase();
				const itemsResults = itemIds.filter(id => items[id].name.toLowerCase().includes(search) || items[id].category.toLowerCase().includes(search) || items[id].type.toLowerCase().includes(search) || id == search);
				$('#itemlog-container img').addClass('d-none');
				for (let i = 0; i < itemsResults.length; i++) {
					document.getElementById('item-log-img-' + itemsResults[i]).classList.remove('d-none');
				}
			}

			function clearItemLogSearch() {
				$('#searchTextboxItemLog').val('');
				updateItemLogSearch($('#searchTextboxItemLog').val());
			}

			$( "#clearItemLog" ).click(function() {
				clearItemLogSearch();
			});

			$(document).ready(function(){
				$("#searchTextboxItemLog").on("keyup", function() {
					updateItemLogSearch($(this).val());
				});
			});

		function openItemLogExtra() {
			let timesFound = (timesSold = gpFromSale = deathCount = damageTaken = damageDealt = missedAttacks = timesEaten = healedFor = totalAttacks = amountUsedInCombat = timeWaited = timesDied = timesGrown = harvestAmount = enemiesKilled = timesOpened = '');
			$('#itemlog-container').html('');
			clearItemLogSearch();

		for (let i = 0; i < itemStats.length; i++) {
				let itemTooltip;
				if (itemStats[i].timesFound > 0) {
					timesFound = timesSold = gpFromSale = deathCount = damageTaken = damageDealt = missedAttacks = timesEaten = healedFor = totalAttacks = amountUsedInCombat = timeWaited = timesDied = timesGrown = harvestAmount = enemiesKilled = timesOpened = '';
					if (itemStats[i].timesFound > 0) timesFound = "<br>Times Found: <small class='text-warning'>" + formatNumber(itemStats[i].timesFound) + '</small>';
					if (itemStats[i].timesSold > 0) timesSold = "<br>Quantity Sold: <small class='text-warning'>" + formatNumber(itemStats[i].timesSold) + '</small>';
					if (itemStats[i].gpFromSale > 0) gpFromSale = "<br>GP Gained from sales: <small class='text-warning'>" + formatNumber(itemStats[i].gpFromSale) + '</small>';
					if (itemStats[i].deathCount > 0) deathCount = "<br>Times lost due to death: <small class='text-warning'>" + formatNumber(itemStats[i].deathCount) + '</small>';
					if (itemStats[i].damageTaken > 0) damageTaken = "<br>Damage Taken whilst Equipped: <small class='text-warning'>" + formatNumber(itemStats[i].damageTaken) + '</small>';
					if (itemStats[i].damageDealt > 0) damageDealt = "<br>Damage Dealt: <small class='text-warning'>" + formatNumber(itemStats[i].damageDealt) + '</small>';
					if (itemStats[i].missedAttacks > 0) missedAttacks = "<br>Attacks Missed: <small class='text-warning'>" + formatNumber(itemStats[i].missedAttacks) + '</small>';
					if (itemStats[i].timesEaten > 0) timesEaten = "<br>Times Eaten: <small class='text-warning'>" + formatNumber(itemStats[i].timesEaten) + '</small>';
					if (itemStats[i].healedFor > 0) healedFor = "<br>Healed for: <small class='text-warning'>" + formatNumber(itemStats[i].healedFor) + '</small>';
					if (itemStats[i].totalAttacks > 0) totalAttacks = "<br>Total Attacks: <small class='text-warning'>" + formatNumber(itemStats[i].totalAttacks) + '</small>';
					if (itemStats[i].amountUsedInCombat > 0) amountUsedInCombat = "<br>Amount used in combat: <small class='text-warning'>" + formatNumber(itemStats[i].amountUsedInCombat) + '</small>';
					if (itemStats[i].timeWaited > 0) timeWaited = "<br>Time spent waiting to grow: <small class='text-warning'>" + formatNumber(itemStats[i].timeWaited) + '</small>';
					if (itemStats[i].timesDied > 0) timesDied = "<br>Crop deaths: <small class='text-warning'>" + formatNumber(itemStats[i].timesDied) + '</small>';
					if (itemStats[i].timesGrown > 0) timesGrown = "<br>Successful grows: <small class='text-warning'>" + formatNumber(itemStats[i].timesGrown) + '</small>';
					if (itemStats[i].harvestAmount > 0) harvestAmount = "<br>Amount harvested: <small class='text-warning'>" + formatNumber(itemStats[i].harvestAmount) + '</small>';
					if (itemStats[i].enemiesKilled > 0) enemiesKilled = "<br>Enemies killed: <small class='text-warning'>" + formatNumber(itemStats[i].enemiesKilled) + '</small>';
					if (itemStats[i].timesOpened > 0) timesOpened = "<br>Opened: <small class='text-warning'>" + formatNumber(itemStats[i].timesOpened) + '</small>';

					$('#itemlog-container').append('<img class="skill-icon-sm item-found" id="item-log-img-' + i + '" src="' + items[i].media + '">');
					itemTooltip = "<div class='text-center'>" + items[i].name + "<small class='text-info'> " + timesFound + timesSold + gpFromSale + totalAttacks + missedAttacks + damageDealt + damageTaken + enemiesKilled + amountUsedInCombat + timesEaten + healedFor + timesGrown + timesDied + timeWaited + harvestAmount + timesOpened + '</small></div>';
				} else {
					$('#itemlog-container').append('<a href="https://wiki.melvoridle.com/index.php?title=' + items[i].name + '" target="blank"><img class="skill-icon-sm" id="item-log-img-' + i + '" style="opacity:0.3;filter: grayscale(30%);" src="' + items[i].media + '"></a>');
					itemTooltip = "<div class='text-center'>" + items[i].name + "</div>";
				}
				tippy('#item-log-img-' + i, {
					content: itemTooltip,
					placement: 'bottom',
					allowHTML: true,
					interactive: false,
					animation: false,
				  });
			}
			//updateTooltips();
			$('#modal-item-log').modal('show');
		}

			$('#modal-monster-log').find('.block-options').prepend('<button id="toggleMonsters" class="btn btn-sm btn-success">Toggle Found</button>');
			$(document).ready(function(){
			  $("#toggleMonsters").click(function(){
				$(".monster-found").toggle();
			  });
			});

		function openMonsterLogExtra() {
			let damageDealtToPlayer = (damageTakenFromPlayer = killedByPlayer = killedPlayer = hitsToPlayer = hitsFromPlayer = enemyMissed = playerMissed = seen = ranAway = '');
			$('#monsterlog-container').html('');
		   for (let i = 0; i < monsterStats.length; i++) {
				let monsterTooltip;
				if (monsterStats[i].killedByPlayer > 0) {
					damageDealtToPlayer = damageTakenFromPlayer = killedByPlayer = killedPlayer = hitsToPlayer = hitsFromPlayer = enemyMissed = playerMissed = seen = ranAway = '';
					damageTakenFromPlayer = "<br>Total Damage Dealt to Monster: <small class='text-warning'>" + formatNumber(monsterStats[i].damageTakenFromPlayer) + '</small>';
					damageDealtToPlayer = "<br>Total Damage Taken from Monster: <small class='text-warning'>" + formatNumber(monsterStats[i].damageDealtToPlayer) + '</small>';
					killedByPlayer = "<br>Times Slain: <small class='text-warning'>" + formatNumber(monsterStats[i].killedByPlayer) + '</small>';
					killedPlayer = "<br>Times Killed by Monster: <small class='text-warning'>" + formatNumber(monsterStats[i].killedPlayer) + '</small>';
					hitsToPlayer = "<br>Successful hits by Monster: <small class='text-warning'>" + formatNumber(monsterStats[i].hitsToPlayer) + '</small>';
					hitsFromPlayer = "<br>Successful hits to Monster: <small class='text-warning'>" + formatNumber(monsterStats[i].hitsFromPlayer) + '</small>';
					enemyMissed = "<br>Missed Attacks by Monster: <small class='text-warning'>" + formatNumber(monsterStats[i].enemyMissed) + '</small>';
					playerMissed = "<br>Missed Attacks to Monster: <small class='text-warning'>" + formatNumber(monsterStats[i].playerMissed) + '</small>';
					seen = "<br>Times Fought: <small class='text-warning'>" + formatNumber(monsterStats[i].seen) + '</small>';
					ranAway = "<br>Times Ran Away: <small class='text-warning'>" + formatNumber(monsterStats[i].ranAway) + '</small>';
					$('#monsterlog-container').append('<img class="skill-icon-md monster-found" id="monster-log-img-' + i + '"  src="' + getMonsterMedia(i) + '">');
					monsterTooltip = "<div class='text-center'>" + MONSTERS[i].name + "<small class='text-info'> " + seen + killedByPlayer + killedPlayer + damageTakenFromPlayer + damageDealtToPlayer + hitsFromPlayer + hitsToPlayer + playerMissed + enemyMissed + ranAway + "</small></div>";
				} else {
					$('#monsterlog-container').append('<a href="https://wiki.melvoridle.com/index.php?title=' + MONSTERS[i].name + '" target="blank"><img class="skill-icon-md" id="monster-log-img-' + i + '" style="opacity:0.3;filter: grayscale(30%);" src="' + getMonsterMedia(i) + '"></a>');
					monsterTooltip = "<div class='text-center'>" + MONSTERS[i].name + "</div>";
				}
				tippy('#monster-log-img-' + i, {
					content: monsterTooltip,
					placement: 'bottom',
					allowHTML: true,
					interactive: false,
					animation: false,
				  });
			}
			updateTooltips();
			$('#modal-monster-log').modal('show');
		}

			$('#modal-pet-log').find('.block-options').prepend('<button id="togglePets" class="btn btn-sm btn-success">Toggle Found</button>');
			$(document).ready(function(){
			  $("#togglePets").click(function(){
				$(".pet-found").toggle();
			  });
			});

		function openPetLogExtra() {
			$('#petlog-container').html('');
			for (let i = 0; i < PETS.length; i++) {
				let tooltop;
				if (petUnlocked[i]) {
					$('#petlog-container').append('<img class="skill-icon-md pet-found" id="pet-log-img-' + i + '" src="' + PETS[i].media + '">');
						tooltip = '<div class="text-center"><span class="text-warning">' + PETS[i].name + '</span><br><span class="text-info">' + PETS[i].description + '</span></div>';
				} else {
					$('#petlog-container').append('<a href="https://wiki.melvoridle.com/index.php?title=' + PETS[i].name + '" target="blank"><img class="skill-icon-md" id="pet-log-img-' + i + '" style="opacity:0.3;filter: grayscale(50%);" src="' + PETS[i].media + '"></a>');
					tooltip = '<div class="text-center">' + PETS[i].name + '<br><small class=\'text-danger\'>Hint: ' + PETS[i].acquiredBy + '</small></div>';
				}
				tippy('#pet-log-img-' + i, {
					content: tooltip,
					placement: 'bottom',
					allowHTML: true,
					interactive: false,
					animation: false,
				  });
			}
			$('#modal-pet-log').modal('show');
		}
		function showMasteryProgressExtra(skill, masteryArray) {
			$('#modal-content-mastery').html('');
			let masteryHtml = '';
			masteryHtml += '<div class="block block-rounded block-link-pop border-top border-' + setToLowercase(skillName[skill]) + ' border-4x">';
			masteryHtml += '<div class="block-header">';
			masteryHtml += '<h3 class="block-title"><img class="mastery-icon-xs mr-2" src="assets/media/main/mastery_header.svg">' + skillName[skill] + '</h3>';
			masteryHtml += '<div class="block-options"><button type="button" class="btn-block-option" data-dismiss="modal" aria-label="Close"><i class="fa fa-fw fa-times"></i></button></div>';
			masteryHtml += '</div>';
			masteryHtml += '<div class="row"><div class="col-12"><div class="block-content">';
			masteryHtml += '<table class="table table-sm table-vcenter">';
			masteryHtml += '<thead><tr><th class="text-center" style="width: 65px;">Item</th><th class="text-center" style="width: 65px;">Level</th><th>Progress</th></tr></thead><tbody>';
			let currentLevelXP;
			let nextLevelXP;
			let nextMasteryProgress;
			if (skill === CONSTANTS.skill.Smithing) {
				for (let i = 0; i < smithingSorted.length; i++) {
					masteryHtml += '<tr>';
					masteryHtml += '<th class="text-center" scope="row">';
					if (masteryArray[smithingSorted[i].smithingID].masteryXP < 1) {
						let media = getMasteryMedia(i, skill);
						masteryHtml += '<img class="milestone-icon" style="opacity:0.3;filter: grayscale(50%);" src="' + media + '">';
					} else {
						let media = getMasteryMedia(i, skill);
						masteryHtml += '<img class="milestone-icon" src="' + media + '">';
					}
					masteryHtml += '</th>';
					masteryHtml += '<th class="text-center" scope="row">';
					masteryHtml += masteryArray[smithingSorted[i].smithingID].mastery;
					masteryHtml += '</th>';
					masteryHtml += '<td class="font-w600 font-size-sm">';
					masteryHtml += '<div class="progress active" style="height: 10px">';
					currentLevelXP = masteryExp.level_to_xp(masteryArray[smithingSorted[i].smithingID].mastery);
					nextLevelXP = masteryExp.level_to_xp(masteryArray[smithingSorted[i].smithingID].mastery + 1);
					//Figure out next level progress percentage
					nextMasteryProgress = (masteryArray[smithingSorted[i].smithingID].masteryXP - currentLevelXP) / (nextLevelXP - currentLevelXP) * 100;
					masteryHtml += '<div class="progress-bar bg-info" role="progressbar" style="width: ' + nextMasteryProgress + '%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">';
					masteryHtml += '</div></div></td></tr>';
				}
			} else {
				for (let i = 0; i < masteryArray.length; i++) {
					masteryHtml += '<tr>';
					masteryHtml += '<th class="text-center" scope="row">';
					if (masteryArray[i].masteryXP < 1) {
						let media = getMasteryMedia(i, skill);
						masteryHtml += '<img class="milestone-icon" style="opacity:0.3;filter: grayscale(50%);" src="' + media + '">';
					} else {
						let media = getMasteryMedia(i, skill);
						masteryHtml += '<img class="milestone-icon" src="' + media + '">';
					}
					masteryHtml += '</th>';
					if (masteryArray[i].mastery < 99) masteryHtml += '<th class="text-center" scope="row">';
					else masteryHtml += '<th class="text-center text-complete" scope="row">';
					if (masteryArray[i].mastery > 99) masteryHtml += '99';
					else masteryHtml += masteryArray[i].mastery;
					masteryHtml += '</th>';
					masteryHtml += '<td class="font-w600 font-size-sm">';
					masteryHtml += '<div class="progress active" style="height: 10px">';
					currentLevelXP = masteryExp.level_to_xp(masteryArray[i].mastery);
					nextLevelXP = masteryExp.level_to_xp(masteryArray[i].mastery + 1);
					if (masteryArray[i].mastery < 99) {
						nextMasteryProgress = (masteryArray[i].masteryXP - currentLevelXP) / (nextLevelXP - currentLevelXP) * 100;
						masteryHtml += '<div class="progress-bar bg-info" role="progressbar" style="width: ' + nextMasteryProgress + '%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">';
					} else {
						nextMasteryProgress = 100;
						masteryHtml += '<div class="progress-bar bg-success" role="progressbar" style="width: ' + nextMasteryProgress + '%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">';
					}
					masteryHtml += '</div></div></td></tr>';
				}
			}
			masteryHtml += '</tbody></table></div></div></div></div>';
			$('#modal-content-mastery').append(masteryHtml);
			$('#modal-mastery').modal('show');
		}

		window.openItemLog = function() {
			openItemLogExtra();
		};

		window.openMonsterLog = function() {
			openMonsterLogExtra();
		};

		window.openPetLog = function() {
			openPetLogExtra();
		};

		window.showMasteryProgress = function(...args) {
			showMasteryProgressExtra(...args);
		};
	}

	function loadScript() {
		if (window.isLoaded || (typeof unsafeWindow !== 'undefined' && unsafeWindow.isLoaded)) {
			clearInterval(scriptLoader);
			injectScript(script);
		}
	}

	const scriptLoader = setInterval(loadScript, 200);
})();