您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows time remaining for completing a task with your current resources. Takes into account Mastery Levels and other bonuses.
当前为
// ==UserScript== // @name Melvor TimeRemaining // @namespace http://tampermonkey.net/ // @version 0.5.0 // @description Shows time remaining for completing a task with your current resources. Takes into account Mastery Levels and other bonuses. // @author Breindahl#2660 // @match https://melvoridle.com/* // @match https://www.melvoridle.com/* // @match https://melvoridle.com/* // @match https://test.melvoridle.com/* // @grant none // ==/UserScript== /* jshint esversion: 9 */ // Note that this script is made for Melvor Idle version 0.17 // Later versions might break parts of this script // Big thanks to Xhaf#6478 and 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 TimeRemaining Loaded'); // Function to send notifications function notify(msg) { One.helpers('notify', { type: 'dark', from: 'bottom', align: 'center', message: msg }); } // Funtion to check if task is complete function taskComplete() { if (window.timeLeftLast > 1 && window.timeLeftCurrent === 0) { notify("Task Done"); console.log('Melvor TimeRemaining: task done'); let ding = new Audio("https://www.myinstants.com/media/sounds/ding-sound-effect.mp3"); ding.volume=0.1; ding.play(); } } // Create timeLeft containers let TempContainer = ['<div class="font-size-sm font-w600 text-uppercase text-center text-muted"><small id ="','" class="mb-2" style="display:block;clear:both;white-space: pre-line" data-toggle="tooltip" data-placement="top" data-html="true" title="" data-original-title=""></small></div>']; let TempContainerAlt = ['<div class="font-size-sm text-uppercase text-muted"><small id ="','" class="mt-2" style="display:block;clear:both;white-space: pre-line" data-toggle="tooltip" data-placement="top" data-html="true" title="" data-original-title=""></small></div>']; $("#smith-item-have").after(TempContainer[0] + "timeLeftSmithing" + TempContainer[1]); $("#fletch-item-have").after(TempContainer[0] + "timeLeftFletching" + TempContainer[1]); $("#runecraft-item-have").after(TempContainer[0] + "timeLeftRunecrafting" + TempContainer[1]); $("#craft-item-have").after(TempContainer[0] + "timeLeftCrafting" + TempContainer[1]); $("#herblore-item-have").after(TempContainer[0] + "timeLeftHerblore" + TempContainer[1]); $("#skill-cooking-food-selected-qty").after(TempContainerAlt[0] + "timeLeftCooking" + TempContainerAlt[1]); $("#skill-fm-logs-selected-qty").after(TempContainerAlt[0] + "timeLeftFiremaking" + TempContainerAlt[1]); $("#magic-item-have-and-div").after(TempContainer[0] + "timeLeftMagic" + TempContainer[1]); // Funtion to get unformatted number for Qty function getQtyUnformat(itemID) { for (let i = 0; i < bank.length; i++) { if (bank[i].id === itemID) { return bank[i].qty; } } return 0; } // Convert seconds to hours/minutes/seconds and format them function secondsToHms(d) { d = Number(d); let h = Math.floor(d / 3600); let m = Math.floor(d % 3600 / 60); let s = Math.floor(d % 3600 % 60); let sDisplay = s > 0 ? s + (s == 1 ? " second" : " seconds") : ""; let mDisplay = m > 0 ? m + (m == 1 ? " minute" : " minutes") : ""; let mDisplayComma = m > 0 && s > 0 ? " and " : ""; let hDisplay = h > 0 ? h + (h == 1 ? " hour" : " hours") : ""; let hDisplayComma = h > 0 ? ((m === 0 && s > 0)||(s === 0 && m > 0) ? " and " : ((s > 0 || m > 0) ? ", " : "")) : ""; return hDisplay + hDisplayComma + mDisplay + mDisplayComma + sDisplay; } // Add seconds to date function AddSecondsToDate(date, seconds) { return new Date(date.getTime() + seconds*1000); } // Format date function DateFormat(date,time){ let days = Math.floor(time / 86400); days = days > 0 ? ' + ' + days + ' days': ''; let hours = date.getHours(); hours = hours < 10 ? '0' + hours : hours; let minutes = date.getMinutes(); minutes = minutes < 10 ? '0' + minutes : minutes; let strTime = hours + ':' + minutes + days; return strTime; } // Convert level to XP needed to reach that level function convertLvlToXP(level) { if(level === Infinity) { return Infinity; } let xp = 0; if (level === 1) {return xp;} xp = exp.level_to_xp(level)+1; return xp; } // Convert XP value to level function convertXPToLvl(xp, noCap = false) { let level = exp.xp_to_level(xp) - 1; if (level < 1) level = 1; if(!noCap) { if (level > 99) level = 99; } return level; } // Main function function timeRemaining(currentSkill){ // Reset variables var masteryID = 0; var skillInterval = 0; // Update interval of skill var rhaelyxCharge = 0; var chargeUses = 0; var itemXP = 0; var item = 0; var initialTotalMasteryPoolXP = 0; var masteryPoolMaxXP = 0; var initialTotalMasteryLevelForSkill = 0; var initialTotalMasteryXP = 0; // Current amount of Mastery experience var masteryLim = []; // Xp needed to reach next level var skillLim = []; // Xp needed to reach next level var poolLim = []; // Xp need to reach next pool checkpoint var skillReq = []; // Needed items for craft and their quantities var itemCraft = []; // Amount of items craftable for each resource requirement var recordCraft = Infinity; // Amount of craftable items for limiting resource // Generate default values for script var timeLeftID = "timeLeft".concat(currentSkill); // Field for generating timeLeft HTML var masteryLimLevel = Array.from({ length: 98 }, (_, i) => i + 2); //Breakpoints for mastery bonuses - default all levels starting at 2 to 99, followed by Infinity masteryLimLevel.push(Infinity); var skillLimLevel = Array.from({ length: 98 }, (_, i) => i + 2); //Breakpoints for mastery bonuses - default all levels starting at 2 to 99, followed by Infinity skillLimLevel.push(Infinity); var poolLimCheckpoints = [10,25,50,95,Infinity]; //Breakpoints for mastery pool bonuses followed by Infinity var chanceToKeep = Array.from({ length: 99 }, (_, i) => i *0.002); // Chance to keep at breakpoints - default 0.2% per level chanceToKeep[98] += 0.05; // Level 99 Bonus var now = new Date(); // Current time and day var skillID = skillName.indexOf(currentSkill); // ID of skill var initialSkillXP = skillXP[skillID]; // Current skill XP // Set current skill and pull matching variables from game with script if (currentSkill == "Smithing") { item = smithingItems[selectedSmith].itemID; itemXP = items[item].smithingXP; skillInterval = 2000; if (godUpgrade[3]) skillInterval *= 0.8; for (let i of items[item].smithReq) { skillReq.push(i); } masteryLimLevel = [20,40,60,80,99,Infinity]; // Smithing Mastery Limits chanceToKeep = [0,0.05,0.10,0.15,0.20,0.30]; //Smithing Mastery bonus percentages if(petUnlocked[5]) chanceToKeep = chanceToKeep.map(n => n + PETS[5].chance/100); // Add Pet Bonus } if (currentSkill == "Fletching") { item = fletchingItems[selectedFletch].itemID; itemXP = items[item].fletchingXP; skillInterval = 2000; if (godUpgrade[0]) skillInterval *= 0.8; if (petUnlocked[8]) skillInterval -= 200; for (let i of items[item].fletchReq) { skillReq.push(i); } //Special Case for Arrow Shafts if (item == 276) { if (selectedFletchLog === undefined) {selectedFletchLog = 0;} skillReq = [skillReq[selectedFletchLog]]; } } if (currentSkill == "Runecrafting") { item = runecraftingItems[selectedRunecraft].itemID; itemXP = items[item].runecraftingXP; skillInterval = 2000; if (godUpgrade[1]) skillInterval *= 0.8; for (let i of items[item].runecraftReq) { skillReq.push(i); } masteryLimLevel = [Infinity]; // Runecrafting has no Mastery bonus chanceToKeep = [0]; //Thus no chance to keep if (equippedItems.includes(CONSTANTS.item.Runecrafting_Skillcape) || equippedItems.includes(CONSTANTS.item.Max_Skillcape) || equippedItems.includes(CONSTANTS.item.Cape_of_Completion)) chanceToKeep[0] += 0.35; if (petUnlocked[10]) chanceToKeep[0] += PETS[10].chance/100; } if (currentSkill == "Crafting") { item = craftingItems[selectedCraft].itemID; itemXP = items[item].craftingXP; skillInterval = 3000; if (godUpgrade[0]) skillInterval *= 0.8; if (equippedItems.includes(CONSTANTS.item.Crafting_Skillcape) || equippedItems.includes(CONSTANTS.item.Max_Skillcape) || equippedItems.includes(CONSTANTS.item.Cape_of_Completion)) skillInterval -= 500; if (petUnlocked[9]) skillInterval -= 200; for (let i of items[item].craftReq) { skillReq.push(i); } } if (currentSkill == "Herblore") { item = herbloreItemData[selectedHerblore].itemID[getHerbloreTier(selectedHerblore)]; itemXP = herbloreItemData[selectedHerblore].herbloreXP; skillInterval = 2000; if (godUpgrade[1]) skillInterval *= 0.8; for (let i of items[item].herbloreReq) { skillReq.push(i); } } if (currentSkill == "Cooking") { item = selectedFood; itemXP = items[item].cookingXP; if (currentCookingFire > 0) { itemXP *= (1 + cookingFireData[currentCookingFire - 1].bonusXP / 100); } skillInterval = 3000; if (godUpgrade[3]) skillInterval *= 0.8; skillReq = [{id: item, qty: 1}]; masteryLimLevel = [Infinity]; //Cooking has no Mastery bonus chanceToKeep = [0]; //Thus no chance to keep } if (currentSkill == "Firemaking") { item = selectedLog; let bonfireBonus = logsData[selectedLog].bonfireBonus; itemXP = logsData[selectedLog].xp + logsData[selectedLog].xp * (bonfireBonus / 100); skillInterval = logsData[selectedLog].interval; if (godUpgrade[3]) skillInterval *= 0.8; skillReq = [{id: item, qty: 1}]; chanceToKeep.fill(0); // Firemaking Mastery does not provide preservation chance } if (currentSkill == "Magic") { skillInterval = 2000; //Find need runes for spell altMagicID = selectedAltMagic; if (ALTMAGIC[selectedAltMagic].runesRequiredAlt !== undefined && useCombinationRunes) { for (let i of ALTMAGIC[selectedAltMagic].runesRequiredAlt) { skillReq.push({...i}); } } else { for (let i of ALTMAGIC[selectedAltMagic].runesRequired) { skillReq.push({...i}); } } // Get Rune discount for (let i = 0; i < skillReq.length; i++) { if (items[equippedItems[CONSTANTS.equipmentSlot.Weapon]].providesRune !== undefined) { if (items[equippedItems[CONSTANTS.equipmentSlot.Weapon]].providesRune.includes(skillReq[i].id)) { let capeMultiplier = 1; if (equippedItems.includes(CONSTANTS.item.Magic_Skillcape) || equippedItems.includes(CONSTANTS.item.Max_Skillcape) || equippedItems.includes(CONSTANTS.item.Cape_of_Completion)) capeMultiplier = 2; // Add cape multiplier skillReq[i].qty -= items[equippedItems[CONSTANTS.equipmentSlot.Weapon]].providesRuneQty * capeMultiplier; } } } skillReq = skillReq.filter(item => item.qty > 0); // Remove all runes with 0 cost //Other items if (ALTMAGIC[selectedAltMagic].selectItem == 1 && selectedMagicItem[1] !== null) { // Spells that just use 1 item skillReq.push({id: selectedMagicItem[1], qty: 1}); } if (ALTMAGIC[selectedAltMagic].selectItem == -1) { // Spells that dont require you to select an item if (ALTMAGIC[selectedAltMagic].needCoal) { // Rags to Riches II skillReq.push({id: 48, qty: 1}); } } if (selectedMagicItem[0] !== null && ALTMAGIC[selectedAltMagic].selectItem == 0) { // SUPERHEAT for (let i of items[selectedMagicItem[0]].smithReq) { skillReq.push({...i}); } if (ALTMAGIC[selectedAltMagic].ignoreCoal) { skillReq = skillReq.filter(item => item.id !== 48); } } masteryLimLevel = [Infinity]; //AltMagic has no Mastery bonus chanceToKeep = [0]; //Thus no chance to keep } // Configure initial mastery values for all skills with masteries if (currentSkill != "Magic") { initialTotalMasteryPoolXP = MASTERY[skillID].pool; masteryPoolMaxXP = getMasteryPoolTotalXP(skillID); initialTotalMasteryLevelForSkill = getCurrentTotalMasteryLevelForSkill(skillID); masteryID = items[item].masteryID[1]; initialTotalMasteryXP = MASTERY[skillID].xp[masteryID]; } // Apply itemXP Bonuses from gear and pets itemXP = addXPBonuses(skillID, itemXP, true); // Populate masteryLim from masteryLimLevel for (let i = 0; i < masteryLimLevel.length; i++) { masteryLim[i] = convertLvlToXP(masteryLimLevel[i]); } // Populate skillLim from skillLimLevel for (let i = 0; i < skillLimLevel.length; i++) { skillLim[i] = convertLvlToXP(skillLimLevel[i]); } // Populate poolLim from masteryCheckpoints for (let i = 0; i < poolLimCheckpoints.length; i++) { poolLim[i] = masteryPoolMaxXP * poolLimCheckpoints[i] / 100; } // Check for Crown of Rhaelyx var RhaelyxChance = 0.15; if (equippedItems.includes(CONSTANTS.item.Crown_of_Rhaelyx) && currentSkill != "Magic") { for (let i = 0; i < masteryLimLevel.length; i++) { chanceToKeep[i] += 0.10; // Add base 10% chance } rhaelyxCharge = getQtyUnformat(CONSTANTS.item.Charge_Stone_of_Rhaelyx); chargeUses = rhaelyxCharge * 1000; // Estimated uses from Rhaelyx Charge Stones } // Get Item Requirements and Current Requirements for (let i = 0; i < skillReq.length; i++) { let itemReq = skillReq[i].qty; //Check how many of required resourse in Bank let itemQty = getQtyUnformat(skillReq[i].id); // Calculate max items you can craft for each itemReq itemCraft[i] = Math.floor(itemQty/itemReq); // Calculate limiting factor and set new record if(itemCraft[i] < recordCraft) { recordCraft = itemCraft[i]; } } //Return the chanceToKeep for any mastery EXP function masteryChance(masteryEXP, chanceToRefTable){ let chanceTo = chanceToRefTable; if (masteryEXP >= masteryLim[0]) { for (let i = 0; i < masteryLim.length; i++) { if (masteryLim[i] <= masteryEXP && masteryEXP < masteryLim[i+1]) { return chanceTo[i+1]; } } } else {return chanceTo[0];} } // Adjust interval based on unlocked bonuses function intervalAdjustment(currentPoolMasteryXP, currentMasteryXP) { let adjustedInterval = skillInterval; if (currentSkill == "Smithing") { //none } if (currentSkill == "Fletching") { if (currentPoolMasteryXP >= poolLim[3]) adjustedInterval -= 200; } if (currentSkill == "Runecrafting") { //none } if (currentSkill == "Crafting") { //none } if (currentSkill == "Herblore") { //none } if (currentSkill == "Cooking") { //none } if (currentSkill == "Firemaking") { if (currentPoolMasteryXP >= poolLim[1]) adjustedInterval *= 0.9; let descreasedBurnInterval = 1 - convertXPToLvl(currentMasteryXP) * 0.001; adjustedInterval *= descreasedBurnInterval; } return adjustedInterval; } // Adjust preservation chance based on unlocked bonuses function preservationAdjustment(currentPoolMasteryXP) { let adjustedPreservation = 0; if (currentSkill == "Smithing") { if (currentPoolMasteryXP >= poolLim[1]) adjustedPreservation += 5; if (currentPoolMasteryXP >= poolLim[2]) adjustedPreservation += 5; } if (currentSkill == "Fletching") { //none } if (currentSkill == "Runecrafting") { if (currentPoolMasteryXP >= poolLim[2]) adjustedPreservation += 10; } if (currentSkill == "Crafting") { //none } if (currentSkill == "Herblore") { if (currentPoolMasteryXP >= poolLim[2]) adjustedPreservation += 5; } if (currentSkill == "Cooking") { if (currentPoolMasteryXP >= poolLim[2]) adjustedPreservation += 10; } if (currentSkill == "Firemaking") { } return adjustedPreservation / 100; } // Adjust skill XP based on unlocked bonuses function skillXPAdjustment(currentPoolMasteryXP, currentMasteryXP) { let xpMultiplier = 1; if (currentSkill == "Smithing") { //none } if (currentSkill == "Fletching") { //none } if (currentSkill == "Runecrafting") { if (currentPoolMasteryXP >= poolLim[1]) xpMultiplier += 1.5; } if (currentSkill == "Crafting") { //none } if (currentSkill == "Herblore") { //if (currentPoolMasteryXP >= poolLim[1]) xpMultiplier += 0.03; } if (currentSkill == "Cooking") { let burnchance = calcburnChance(currentMasteryXP); let cookXP = itemXP * (1 - burnchance); let burnXP = 1 * burnchance; return cookXP + burnXP; } if (currentSkill == "Firemaking") { // none } return itemXP * xpMultiplier; } // Calculate total number of unlocked items for skill based on current skill level function calcTotalUnlockedItems(currentTotalSkillXP) { let count = 0; let currentSkillLevel = convertXPToLvl(currentTotalSkillXP); for (let i = 0; i < MILESTONES[skillName[skillID]].length; i++) { if (currentSkillLevel >= MILESTONES[skillName[skillID]][i].level) count++; } return count; } // Calculate mastery xp based on unlocked bonuses function calcMasteryXpToAdd(timePerAction, currentTotalSkillXP, currentMasteryXP, currentPoolMasteryXP, currentTotalMasteryLevelForSkill) { let xpModifier = 1; let xpToAdd = (((calcTotalUnlockedItems(skillID, currentTotalSkillXP) * currentTotalMasteryLevelForSkill) / getTotalMasteryLevelForSkill(skillID) + convertXPToLvl(currentMasteryXP) * (getTotalItemsInSkill(skillID) / 10)) * (timePerAction / 1000)) / 2; // General Mastery XP formula if (currentPoolMasteryXP >= poolLim[0]) xpModifier += 0.05; // If current skill is Firemaking, we need to apply mastery progression from actions and use updated currentPoolMasteryXP values if (currentSkill == "Firemaking") { if (currentPoolMasteryXP >= poolLim[3]) xpModifier += 0.05; for (let i = 0; i < MASTERY[CONSTANTS.skill.Firemaking].xp.length; i++) { // The logs you are not burning if (masteryID != i) { if (getMasteryLevel(CONSTANTS.skill.Firemaking, i) >= 99) xpModifier += 0.0025; } } // The log you are burning if (convertXPToLvl(currentMasteryXP) >= 99) xpModifier += 0.0025; } // For all other skills, you use the game function to grab your FM mastery progression else { if (getMasteryPoolProgress(CONSTANTS.skill.Firemaking) >= poolLim[3]) xpModifier += 0.05; for (let i = 0; i < MASTERY[CONSTANTS.skill.Firemaking].xp.length; i++) { if (getMasteryLevel(CONSTANTS.skill.Firemaking, i) >= 99) xpModifier += 0.0025; } } if (petUnlocked[21]) xpModifier += 0.03; if (equippedItems.includes(CONSTANTS.item.Ancient_Ring_Of_Mastery)) xpModifier += items[CONSTANTS.item.Ancient_Ring_Of_Mastery].bonusMasteryXP; xpToAdd *= xpModifier; if (xpToAdd < 1) xpToAdd = 1; // Burnchance affects mastery XP if (currentSkill == "Cooking") { let burnchance = calcburnChance(currentMasteryXP); xpToAdd *= (1 - burnchance); } return xpToAdd; } // Calculate pool XP based on mastery XP function calcPoolXPToAdd(currentTotalSkillXP, masteryXP) { if (convertXPToLvl(currentTotalSkillXP) >= 99) {return masteryXP / 2; } else { return masteryXP / 4; } } // Calculate burn chance based on mastery level function calcburnChance(currentMasteryXP) { let burnChance = 0; if (equippedItems.includes(CONSTANTS.item.Cooking_Skillcape) || equippedItems.includes(CONSTANTS.item.Max_Skillcape) || equippedItems.includes(CONSTANTS.item.Cape_of_Completion)) { return burnChance; } if (equippedItems.includes(CONSTANTS.item.Cooking_Gloves)) { return burnChance; } let primaryBurnChance = (30 - convertXPToLvl(currentMasteryXP) * 0.6) / 100; let secondaryBurnChance = 0.01; if (primaryBurnChance <= 0) { return secondaryBurnChance; } burnChance = 1 - (1 - primaryBurnChance) * (1 - secondaryBurnChance); return burnChance; } // adjusting totalChanceToUse base on Rhaelyx charges and number of actions function adjustTotalChanceToUseRhaelyx(totalChanceToUse, expectedActions, resources) { if (chargeUses >= expectedActions || chargeUses >= resources / (totalChanceToUse - RhaelyxChance)) { return totalChanceToUse - RhaelyxChance; //if we have excess uses, then we simply use better chance to keep and move on as usual } else { return resources / (chargeUses / (totalChanceToUse - RhaelyxChance) + (resources - chargeUses) / totalChanceToUse); //the denominator is the "real" expectedXP with Rhaelyx, so the "real" chanceToKeep is essentially found through resources/(resources/chanceToKeep) } } // Calculates expected time, taking into account Mastery Level advancements during the craft function calcExpectedTime(resources){ let sumTime = 0; let currentTotalMasteryXP = initialTotalMasteryXP; let currentTotalSkillXP = initialSkillXP; let currentTotalPoolXP = initialTotalMasteryPoolXP; let currentTotalMasteryLevelForSkill = initialTotalMasteryLevelForSkill; while (resources > 0) { // Adjustments let currentPreservationAdjustment = preservationAdjustment(currentTotalPoolXP); let totalChanceToUse = 1 - masteryChance(currentTotalMasteryXP,chanceToKeep) - currentPreservationAdjustment; let currentInterval = intervalAdjustment(currentTotalPoolXP, currentTotalMasteryXP); // Current Limits let currentMasteryLim = masteryLim.find(element => element > currentTotalMasteryXP); let currentSkillLim = skillLim.find(element => element > currentTotalSkillXP); let currentPoolLim = poolLim.find(element => element > currentTotalPoolXP); // Current XP let currentMasteryXP = calcMasteryXpToAdd(currentInterval, currentTotalSkillXP, currentTotalMasteryXP, currentTotalPoolXP, currentTotalMasteryLevelForSkill); let currentSkillXP = skillXPAdjustment(currentTotalPoolXP, currentMasteryXP); let currentPoolXP = calcPoolXPToAdd(currentTotalSkillXP, currentMasteryXP); // Distance to Limits let masteryXPToLimit = currentMasteryLim - currentTotalMasteryXP; let skillXPToLimit = currentSkillLim - currentTotalSkillXP; let poolXPToLimit = currentPoolLim - currentTotalPoolXP; // Actions to limits let masteryXPActions = masteryXPToLimit / currentMasteryXP; let skillXPActions = skillXPToLimit / currentSkillXP; let poolXPActions = poolXPToLimit / currentPoolXP; let resourceActions = Math.round(resources / adjustTotalChanceToUseRhaelyx(totalChanceToUse, resources, resources)); // Mininum actions based on limits let expectedActions = Math.ceil(Math.min(masteryXPActions, skillXPActions, poolXPActions, resourceActions)); // Take away resources based on expectedActions if (expectedActions == resourceActions) { resources = 0; // No more limits } else { resources -= Math.round(expectedActions*adjustTotalChanceToUseRhaelyx(totalChanceToUse, expectedActions, resources)); } // Update time and XP sumTime += expectedActions * currentInterval; currentTotalMasteryXP += currentMasteryXP*expectedActions; currentTotalSkillXP += currentSkillXP*expectedActions; currentTotalPoolXP += currentPoolXP*expectedActions; // Update remaining Rhaelyx Charge uses chargeUses -= expectedActions; if ( chargeUses < 0 ) chargeUses = 0; // Level up mastery if hitting Mastery limit if ( masteryXPActions == expectedActions ) currentTotalMasteryLevelForSkill++; } return {"timeLeft" : Math.round(sumTime), "finalSkillLevel" : convertXPToLvl(currentTotalSkillXP,true), "finalMasteryLevel" : convertXPToLvl(currentTotalMasteryXP), "finalPoolPercentage" : Math.min((currentTotalPoolXP/masteryPoolMaxXP)*100,100).toFixed(2)}; } var results = calcExpectedTime(recordCraft); //Time left var timeLeft = 0; if (currentSkill == "Magic") { timeLeft = Math.round(recordCraft * skillInterval / 1000); } else { timeLeft = Math.round(results.timeLeft / 1000); } //Global variables to keep track of when a craft is complete window.timeLeftLast = window.timeLeftCurrent; window.timeLeftCurrent = timeLeft; //Inject timeLeft HTML let timeLeftElement = document.getElementById(timeLeftID); if(timeLeftElement !== null) { if (timeLeft !== 0) { let finishedTime = AddSecondsToDate(now,timeLeft); timeLeftElement.textContent = "Will take: " + secondsToHms(timeLeft) + "\r\n Expected finished: " + DateFormat(finishedTime,timeLeft); timeLeftElement.style.display = "block"; } else { // empty and reset if no time timeLeftElement.style.display = "none"; } } // Generate progression Tooltips for all skills that aren't Magic if (currentSkill != "Magic") { if (!timeLeftElement._tippy) { tippy(timeLeftElement, { allowHTML: true, interactive: false, animation: false, }); }; let lightMode = ""; if (!darkMode) lightMode = ' style="color:white;"'; let wrapper = ['<div class="row"><div class="col-8" style="white-space: nowrap;"><h3 class="block-title m-1"' + lightMode + ' >','</h3></div><div class="col-4" style="white-space: nowrap; text-align:right;"><h3 class="block-title m-1"' + lightMode + '><span class="p-1 bg-',' rounded" style="text-align:center; display: inline-block;line-height: normal;width: 70px;">','</span></h3></div></div>']; let finalSkillLevel = wrapper[0] + 'Final Skill Level ' + wrapper[1] + 'success' + wrapper[2] + results.finalSkillLevel + ' / 99' + wrapper[3]; let finalMasteryLevel = wrapper[0] + 'Final Mastery Level ' + wrapper[1] + 'info' + wrapper[2] + results.finalMasteryLevel + ' / 99' + wrapper[3]; let finalPoolPercentage = wrapper[0] + 'Final Mastery Pool ' + wrapper[1] + 'warning' + wrapper[2] + results.finalPoolPercentage + '%' + wrapper[3]; let tooltip = '<div class="col-12 mt-1">' + finalSkillLevel + finalMasteryLevel + finalPoolPercentage + '</div>'; timeLeftElement._tippy.setContent(tooltip); } } // ## SMITHING ## var selectSmithRef = selectSmith; window.selectSmith = function(...args) { selectSmithRef(...args); try { timeRemaining("Smithing"); } catch (e) { console.error(e); } }; var startSmithingRef = startSmithing; window.startSmithing = function(...args) { startSmithingRef(...args); try { timeRemaining("Smithing"); taskComplete(); } catch (e) { console.error(e); } }; // ## FLETCHING ## var selectFletchRef = selectFletch; window.selectFletch = function(...args) { selectFletchRef(...args); try { timeRemaining("Fletching"); } catch (e) { console.error(e); } }; var startFletchingRef = startFletching; window.startFletching = function(...args) { startFletchingRef(...args); try { timeRemaining("Fletching"); taskComplete(); } catch (e) { console.error(e); } }; // ## RUNECRAFTING ## var selectRunecraftRef = selectRunecraft; window.selectRunecraft = function(...args) { selectRunecraftRef(...args); try { timeRemaining("Runecrafting"); } catch (e) { console.error(e); } }; var startRunecraftingRef = startRunecrafting; window.startRunecrafting = function(...args) { startRunecraftingRef(...args); try { timeRemaining("Runecrafting"); taskComplete(); } catch (e) { console.error(e); } }; // ## CRAFTING ## var selectCraftRef = selectCraft; window.selectCraft = function(...args) { selectCraftRef(...args); try { timeRemaining("Crafting"); } catch (e) { console.error(e); } }; var startCraftingRef = startCrafting; window.startCrafting = function(...args) { startCraftingRef(...args); try { timeRemaining("Crafting"); taskComplete(); } catch (e) { console.error(e); } }; // ## HERBLORE ## var selectHerbloreRef = selectHerblore; window.selectHerblore = function(...args) { selectHerbloreRef(...args); try { timeRemaining("Herblore"); } catch (e) { console.error(e); } }; var startHerbloreRef = startHerblore; window.startHerblore = function(...args) { startHerbloreRef(...args); try { timeRemaining("Herblore"); taskComplete(); } catch (e) { console.error(e); } }; // ## COOKING ## var selectFoodRef = selectFood; window.selectFood = function(...args) { selectFoodRef(...args); try { timeRemaining("Cooking"); } catch (e) { console.error(e); } }; var startCookingRef = startCooking; window.startCooking = function(...args) { startCookingRef(...args); try { timeRemaining("Cooking"); taskComplete(); } catch (e) { console.error(e); } }; // ## FIREMAKING ## var selectLogRef = selectLog; window.selectLog = function(...args) { selectLogRef(...args); try { timeRemaining("Firemaking"); } catch (e) { console.error(e); } }; var burnLogRef = burnLog; window.burnLog = function(...args) { burnLogRef(...args); try { timeRemaining("Firemaking"); taskComplete(); } catch (e) { console.error(e); } }; // ## ALT MAGIC ## var selectMagicRef = selectMagic; window.selectMagic = function(...args) { selectMagicRef(...args); try { timeRemaining("Magic"); } catch (e) { console.error(e); } }; var selectItemForMagicRef = selectItemForMagic; window.selectItemForMagic = function(...args) { selectItemForMagicRef(...args); try { timeRemaining("Magic"); } catch (e) { console.error(e); } }; var castMagicRef = castMagic; window.castMagic = function(...args) { castMagicRef(...args); try { timeRemaining("Magic"); taskComplete(); } catch (e) { console.error(e); } }; } function loadScript() { if ((window.isLoaded && !window.currentlyCatchingUp) || (typeof unsafeWindow !== 'undefined' && unsafeWindow.isLoaded && !unsafeWindow.currentlyCatchingUp)) { // Only load script after game has opened clearInterval(scriptLoader); injectScript(script); } } const scriptLoader = setInterval(loadScript, 200); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址