// ==UserScript== // @name Mooneycalc-Importer (Multi) // @namespace http://tampermonkey.net/ // @version 2.1.4 // @description 脚本基于Mooneycalc-Importer v5.3,增加了组队模拟功能,使用时请手动查看队友装备以获取数据。https://amvoidguy.github.io/MWICombatSimulatorTest/dist/index.html. // @match https://www.milkywayidle.com/* // @match https://mooneycalc.vercel.app/* // @match https://mooneycalc.netlify.app/* // @match https://amvoidguy.github.io/MWICombatSimulatorTest/dist/index.html // @match https://shykai.github.io/MWICombatSimulatorTest/dist/ // @match http://localhost:9000/ // @run-at document-start // @grant GM_getValue // @grant GM_setValue // @grant GM_listValues // @grant GM_deleteValue // @downloadURL https://update.greasyfork.icu/scripts/507255/Mooneycalc-Importer%20%28Multi%29.user.js // @updateURL https://update.greasyfork.icu/scripts/507255/Mooneycalc-Importer%20%28Multi%29.meta.js // ==/UserScript== //TODO // 1. 目前只支持当然页面刷新之后的角色 模拟多久升级 // 2. 检验trigger是否工作 找到了trigger数据 (function () { "use strict"; const userLanguage = navigator.language || navigator.userLanguage; const isZH = userLanguage.startsWith("zh"); let profile_arr = new Array(); const SCRIPT_SIMULATE_TIME = "24";//模拟时长 //启动前先把所有队友的装备手动看一遍!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (document.URL.includes("milkywayidle.com")) { hookWS(); } else if (document.URL.includes("mooneycalc")) { addImportButton1(); } else if( document.URL.includes("MWICombatSimulatorTest") || document.URL.includes("localhost")){ addImportButton4(); waitSimulationResultsLoading(); } function waitSimulationResultsLoading(){ const waitForNavi = () => { if(document.querySelector(`div.row`)?.querySelectorAll(`div.col-md-5`)?.[2]?.querySelector(`div.row > div.col-md-5`))observeResults(); else setTimeout(waitSimulationResultsLoading, 200); }; waitForNavi(); } function hookWS() { const dataProperty = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data"); const oriGet = dataProperty.get; dataProperty.get = hookedGet; Object.defineProperty(MessageEvent.prototype, "data", dataProperty); function hookedGet() { const socket = this.currentTarget; if (!(socket instanceof WebSocket)) { return oriGet.call(this); } if (socket.url.indexOf("api.milkywayidle.com/ws") <= -1) { return oriGet.call(this); } const message = oriGet.call(this); Object.defineProperty(this, "data", { value: message }); // Anti-loop return handleMessage(message); } } function handleMessage(message) { let obj = JSON.parse(message); //const keys = GM_listValues(); //console.log('keys',keys); // keys.forEach(key => { GM_deleteValue(key); }); if (obj && obj.type === "init_character_data") { if(GM_getValue("profile_arr")) { profile_arr = GM_getValue("profile_arr"); } if(!profile_arr.includes(obj.character.name)){ profile_arr.push(obj.character.name); } // profile_arr.reverse(); // console.log("profileArr",profile_arr); GM_setValue("profile_arr", profile_arr); GM_setValue("profile_" + obj.character.name, message); GM_setValue("init_character_data",message); const init_client_data = localStorage.getItem("initClientData"); if(init_client_data)GM_setValue("init_client_data", init_client_data); }else if (obj && obj.type === "new_battle"){ let newBattleHandler = []; obj.players.forEach((player) => { newBattleHandler.push(player.name); }); console.log("new_battle",newBattleHandler.sort()); //以防万一 留一个旧的team battle GM_setValue("team_battle", message); GM_setValue("team_battle_" + newBattleHandler[0], message); }else if (obj && obj.type === "profile_shared"){ // console.log("profile_shared",obj); GM_setValue(obj.profile.sharableCharacter.name, message); get_sim_json(obj); } return message; } function get_sim_json(obj) { const checkElem = () => { const selectedElement = document.querySelector(`div.SharableProfile_overviewTab__W4dCV`); if (selectedElement) { clearInterval(timer); //获取模拟数据 let player_name=obj.profile.sharableCharacter.name; let exportObj = {}; exportObj.player = {}; exportObj.player.equipment = []; exportObj.houseRooms = {}; //装备 for (const key in obj.profile.wearableItemMap) { const item = obj.profile.wearableItemMap[key]; exportObj.player.equipment.push({ itemLocationHrid: item.itemLocationHrid, itemHrid: item.itemHrid, enhancementLevel: item.enhancementLevel, }); } //别人房子 for (const house of Object.values(obj.profile.characterHouseRoomMap)) { exportObj.houseRooms[house.houseRoomHrid] = house.level; } //技能等级 for (const skill of obj.profile.characterSkills) { if (skill.skillHrid.includes("stamina")) { exportObj.player.staminaLevel = skill.level; } else if (skill.skillHrid.includes("intelligence")) { exportObj.player.intelligenceLevel = skill.level; } else if (skill.skillHrid.includes("attack")) { exportObj.player.attackLevel = skill.level; } else if (skill.skillHrid.includes("power")) { exportObj.player.powerLevel = skill.level; } else if (skill.skillHrid.includes("defense")) { exportObj.player.defenseLevel = skill.level; } else if (skill.skillHrid.includes("ranged")) { exportObj.player.rangedLevel = skill.level; } else if (skill.skillHrid.includes("magic")) { exportObj.player.magicLevel = skill.level; } } //战斗技能等级 exportObj.abilities = [ { abilityHrid: "", level: "1", }, { abilityHrid: "", level: "1", }, { abilityHrid: "", level: "1", }, { abilityHrid: "", level: "1", }, { abilityHrid: "", level: "1", }, ]; let clientObj = JSON.parse(GM_getValue("init_client_data")); let normalAbillityIndex = 1; for (const ability of obj.profile.equippedAbilities) { if (ability && clientObj.abilityDetailMap[ability.abilityHrid].isSpecialAbility) { exportObj.abilities[0] = { abilityHrid: ability.abilityHrid, level: ability.level, }; } else if (ability) { exportObj.abilities[normalAbillityIndex++] = { abilityHrid: ability.abilityHrid, level: ability.level, }; } } //吃喝 exportObj.food={}; exportObj.drinks={}; exportObj.food["/action_types/combat"] = []; exportObj.drinks["/action_types/combat"] = []; let weapon=obj.profile.wearableItemMap["/item_locations/main_hand"]?.itemHrid||obj.profile.wearableItemMap["/item_locations/two_hand"]?.itemHrid; if(weapon.includes("shooter")||weapon.includes("bow")){ //远程 // xp,超远,暴击 exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/wisdom_coffee" }); exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/super_ranged_coffee" }); exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/critical_coffee" }); // 2红1蓝 exportObj.food["/action_types/combat"].push({ itemHrid:"/items/spaceberry_donut" }); exportObj.food["/action_types/combat"].push({ itemHrid:"/items/spaceberry_cake" }); exportObj.food["/action_types/combat"].push({ itemHrid:"/items/star_fruit_yogurt" }); }else if (weapon.includes("boomstick")||weapon.includes("staff")){ //法师 //xp,超魔,吟唱 exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/wisdom_coffee" }); exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/super_magic_coffee" }); exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/channeling_coffee" }); // 1红2蓝 exportObj.food["/action_types/combat"].push({ itemHrid:"/items/spaceberry_cake" }); exportObj.food["/action_types/combat"].push({ itemHrid:"/items/star_fruit_gummy" }); exportObj.food["/action_types/combat"].push({ itemHrid:"/items/star_fruit_yogurt" }); }else if (weapon.includes("bulwark")){ //双手盾 精暮光 //xp,超防,超耐 xp,超智,幸运 exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/wisdom_coffee" }); exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/super_defense_coffee" }); exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/super_stamina_coffee" }); // 2红1蓝 exportObj.food["/action_types/combat"].push({ itemHrid:"/items/spaceberry_donut" }); exportObj.food["/action_types/combat"].push({ itemHrid:"/items/spaceberry_cake" }); exportObj.food["/action_types/combat"].push({ itemHrid:"/items/star_fruit_yogurt" }); }else{ //战士 //xp,超力,迅捷 exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/wisdom_coffee" }); exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/super_power_coffee" }); exportObj.drinks["/action_types/combat"].push({ itemHrid:"/items/swiftness_coffee" }); // 2红1蓝 exportObj.food["/action_types/combat"].push({ itemHrid:"/items/spaceberry_donut" }); exportObj.food["/action_types/combat"].push({ itemHrid:"/items/spaceberry_cake" }); exportObj.food["/action_types/combat"].push({ itemHrid:"/items/star_fruit_yogurt" }); } //队内检测 let team_data = GM_getValue("team_battle", ""); let team_obj = JSON.parse(team_data); if(team_obj){ for(const team_member of team_obj.players){ if(team_member.name==player_name){ for(const food_or_drink of team_member.combatConsumables){ if(food_or_drink.itemHrid.includes('coffee')){ exportObj.drinks["/action_types/combat"].push({ itemHrid:food_or_drink.itemHrid, }); }else{ exportObj.food["/action_types/combat"].push({ itemHrid:food_or_drink.itemHrid, }); } } } } } //我不知道有什么用,以防万一写一下,和原版保持一致 exportObj.food["/action_types/combat"].push({ itemHrid:"", }); exportObj.drinks["/action_types/combat"].push({ itemHrid:"", }); let sim_json_value=JSON.stringify(exportObj); //按钮粘贴 let button = document.createElement("button"); selectedElement.appendChild(button); button.textContent = "获取模拟器导入数据"; button.style.borderRadius = '5px'; button.style.height = '30px'; button.style.backgroundColor = '#4357AF'; button.style.color = 'white'; button.style.boxShadow = 'none'; button.style.border = '0px'; button.onclick = function () { navigator.clipboard.writeText(sim_json_value); button.textContent = "导入数据已粘贴到剪切板"; return false; }; return false; }; }; let timer = setInterval(checkElem, 200); } function addImportButton1() { const checkElem = () => { const selectedElement = document.querySelector(`div[role="tablist"]`); if (selectedElement) { clearInterval(timer); console.log("Mooneycalc-Importer: Found elem"); let button = document.createElement("button"); selectedElement.parentNode.insertBefore(button, selectedElement.nextSibling); button.textContent = isZH ? "导入人物数据 (左边的市场里可以改价差,设置里可启用茶)" : "Import character settings (Refresh game page to update character settings)"; button.style.backgroundColor = "green"; button.style.padding = "5px"; button.onclick = function () { console.log("Mooneycalc-Importer: Button onclick"); importData1(button); return false; }; } }; let timer = setInterval(checkElem, 200); } async function importData1(button) { let data = GM_getValue("init_character_data"); let obj = JSON.parse(data); if (!obj || !obj.characterSkills || !obj.currentTimestamp) { button.textContent = isZH ? "错误:没有人物数据" : "Error: no character settings found"; return; } let ls = constructMooneycalcLocalStorage(obj); localStorage.setItem("settings", ls); let timestamp = new Date(obj.currentTimestamp).getTime(); let now = new Date().getTime(); button.textContent = isZH ? "已导入,人物数据更新时间:" + timeReadable(now - timestamp) + " 前" : "Imported, updated " + timeReadable(now - timestamp) + " ago"; await new Promise((r) => setTimeout(r, 500)); location.reload(); } function constructMooneycalcLocalStorage(obj) { const ls = localStorage.getItem("settings"); let lsObj = JSON.parse(ls); if (!lsObj) { lsObj = {}; } // 人物技能等级 lsObj.state.settings.levels = {}; for (const skill of obj.characterSkills) { lsObj.state.settings.levels[skill.skillHrid] = skill.level; } // 社区全局buff lsObj.state.settings.communityBuffs = {}; for (const buff of obj.communityBuffs) { lsObj.state.settings.communityBuffs[buff.hrid] = buff.level; } // 装备 & 装备强化等级 lsObj.state.settings.equipment = {}; lsObj.state.settings.equipmentLevels = {}; for (const item of obj.characterItems) { lsObj.state.settings.equipment[item.itemLocationHrid.replace("item_locations", "equipment_types")] = item.itemHrid; lsObj.state.settings.equipmentLevels[item.itemLocationHrid.replace("item_locations", "equipment_types")] = item.enhancementLevel; } // 房子 lsObj.state.settings.houseRooms = {}; for (const house of Object.values(obj.characterHouseRoomMap)) { lsObj.state.settings.houseRooms[house.houseRoomHrid] = house.level; } return JSON.stringify(lsObj); } function timeReadable(ms) { const d = new Date(1000 * Math.round(ms / 1000)); function pad(i) { return ("0" + i).slice(-2); } let str = d.getUTCHours() + ":" + pad(d.getUTCMinutes()) + ":" + pad(d.getUTCSeconds()); console.log("Mooneycalc-Importer: " + str); return str; } function addImportButton4() { const checkElem = () => { const selectedElement = document.querySelector(`button#buttonImportExport`); if (selectedElement) { clearInterval(timer); console.log("Mooneycalc-Importer: Found elem"); let profileArr = GM_getValue("profile_arr"); generateComboList(profileArr); let button = document.createElement("button"); selectedElement.parentNode.parentElement.parentElement.insertBefore(button, selectedElement.parentElement.parentElement.nextSibling); button.textContent = isZH ? "导入人物数据(刷新游戏网页更新人物数据)" : "Import character settings (Refresh game page to update character settings)"; button.style.backgroundColor = "green"; button.style.padding = "5px"; button.onclick = function () { console.log("Mooneycalc-Importer: Button onclick"); const getPriceButton = document.querySelector(`button#buttonGetPrices`); if (getPriceButton) { console.log("Click getPriceButton"); getPriceButton.click(); } let checkedProfile = getSelectedValue() importData4(button,checkedProfile); return false; }; } }; let timer = setInterval(checkElem, 200); } function getSelectedValue() { let select = document.getElementById("profile-combo-container"); let selectedValue = select.value; return selectedValue; } async function importData4(button,checkedProfile) { let playerInfo = JSON.parse(GM_getValue("profile_"+ checkedProfile)); let playerArr = []; if ( playerInfo.partyInfo ) { Object.values(playerInfo.partyInfo.sharableCharacterMap).forEach((value,key) => { playerArr.push(value.name); }); }else{ playerArr.push(playerInfo.character.name) } playerArr.sort(); let data = GM_getValue("team_battle_" + playerArr[0], ""); let obj if (!data) { button.textContent = isZH ? "错误:没有人物数据" : "Error: no character settings found"; return; }else{ obj = JSON.parse(data); } const jsonObj = constructImportJsonObj_team(obj,checkedProfile); for(let i=1;i<=jsonObj.player_number;i++){ const importInputElem = document.querySelector(`input#inputSetGroupCombatplayer`+i); importInputElem.value = JSON.stringify(jsonObj.players[i-1]); } document.querySelector(`button#buttonImportSet`).click(); let timestamp = new Date(obj.combatStartTime).getTime(); let now = new Date().getTime(); button.textContent = isZH ? "已导入,人物数据更新时间:" + timeReadable(now - timestamp) + " 前" : "Imported, updated " + timeReadable(now - timestamp) + " ago"; if (document.URL.includes(`//amvoidguy.github.io/`)) { setTimeout(() => { const selectZone = document.querySelector(`select#selectZone`); for (let i = 0; i < selectZone.options.length; i++) { if (selectZone.options[i].value == jsonObj.zone) { selectZone.options[i].selected = true; break; } } const player1Checkbox = document.querySelector(`input#player1.form-check-input.player-checkbox`); const player2Checkbox = document.querySelector(`input#player2.form-check-input.player-checkbox`); const player3Checkbox = document.querySelector(`input#player3.form-check-input.player-checkbox`); switch (jsonObj.player_number) { case 1: player1Checkbox.checked = true; break; case 2: player1Checkbox.checked = true; player2Checkbox.checked = true; break; case 3: player1Checkbox.checked = true; player2Checkbox.checked = true; player3Checkbox.checked = true; break; } const time_set = document.querySelector(`input#inputSimulationTime`); time_set.value=jsonObj.simulationTime //无法获取别人的触发器会报错 const errorModal = document.querySelector('div#errorModal'); if (errorModal) { errorModal.parentNode.removeChild(errorModal); } document.querySelector(`button#buttonStartSimulation`).click(); document.querySelector(`button.btn.btn-secondary`).click(); }, 500); } } function generateComboList(profileArr) { const container = document.getElementById("profile-combo-container"); profileArr.forEach(item => { let option = document.createElement("option"); option.value = item; let labelText = item + " Party:"; let profile = GM_getValue("profile_"+ item); if (!profile) return; let playerInfo = JSON.parse(profile); let partyMember = []; if ( playerInfo.partyInfo ) Object.values(playerInfo.partyInfo.sharableCharacterMap).forEach((value,key) => { partyMember.push(value.name); }); partyMember.reverse(); labelText += partyMember.join(", "); option.textContent = labelText; container.appendChild(option); }) } function constructImportJsonObj_team(obj,checkedProfile) { let clientObj = JSON.parse(GM_getValue("init_client_data")); let init_character_obj = JSON.parse(GM_getValue("profile_"+ checkedProfile)); let exportObj = {}; exportObj.player_number=obj.players.length; exportObj.players = {}; //看一圈所有人配置 for(let player_num=0;player_num div.col-md-5`); if(document.URL.includes("mwisim.github.io")||document.URL.includes("simTest")){ resultDiv = document.querySelectorAll(`div.row`)?.[1]?.querySelectorAll(`div.col-md-5`)?.[2]?.querySelector(`div.row > div.col-md-5`); } const deathDiv = document.querySelector(`div#simulationResultPlayerDeaths`); const expDiv = document.querySelector(`div#simulationResultExperienceGain`); const consumeDiv = document.querySelector(`div#simulationResultConsumablesUsed`); deathDiv.style.backgroundColor = "#FFEAE9"; deathDiv.style.color = "black"; expDiv.style.backgroundColor = "#CDFFDD"; expDiv.style.color = "black"; consumeDiv.style.backgroundColor = "#F0F8FF"; consumeDiv.style.color = "black"; let div = document.createElement("div"); div.id = "tillLevel"; div.style.backgroundColor = "#FFFFE0"; div.style.color = "black"; div.textContent = ""; resultDiv.append(div); new MutationObserver((mutationsList) => { mutationsList.forEach((mutation) => { if (mutation.addedNodes.length >= 3) { handleResult(mutation.addedNodes, div); } }); }).observe(expDiv, { childList: true, subtree: true }); } function handleResult(expNodes, parentDiv) { let perHourGainExp = { stamina: 0, intelligence: 0, attack: 0, power: 0, defense: 0, ranged: 0, magic: 0, }; expNodes.forEach((expNodes) => { if (expNodes.textContent.includes("Stamina")||expNodes.textContent.includes("耐力")) { perHourGainExp.stamina = Number(expNodes.children[1].textContent); } else if (expNodes.textContent.includes("Intelligence")||expNodes.textContent.includes("智力")) { perHourGainExp.intelligence = Number(expNodes.children[1].textContent); } else if (expNodes.textContent.includes("Attack")||expNodes.textContent.includes("攻击")) { perHourGainExp.attack = Number(expNodes.children[1].textContent); } else if (expNodes.textContent.includes("Power")||expNodes.textContent.includes("力量")) { perHourGainExp.power = Number(expNodes.children[1].textContent); } else if (expNodes.textContent.includes("Defense")||expNodes.textContent.includes("防御")) { perHourGainExp.defense = Number(expNodes.children[1].textContent); } else if (expNodes.textContent.includes("Ranged")||expNodes.textContent.includes("远程")) { perHourGainExp.ranged = Number(expNodes.children[1].textContent); } else if (expNodes.textContent.includes("Magic")||expNodes.textContent.includes("魔法")) { perHourGainExp.magic = Number(expNodes.children[1].textContent); } }); let data = GM_getValue("init_character_data"); let obj = JSON.parse(data); if (!obj || !obj.characterSkills || !obj.currentTimestamp) { console.error("handleResult no character localstorage"); return; } let skillLevels = {}; for (const skill of obj.characterSkills) { if (skill.skillHrid.includes("stamina")) { skillLevels.stamina = {}; skillLevels.stamina.skillName = "Stamina"; skillLevels.stamina.currentLevel = skill.level; skillLevels.stamina.currentExp = skill.experience; } else if (skill.skillHrid.includes("intelligence")) { skillLevels.intelligence = {}; skillLevels.intelligence.skillName = "Intelligence"; skillLevels.intelligence.currentLevel = skill.level; skillLevels.intelligence.currentExp = skill.experience; } else if (skill.skillHrid.includes("attack")) { skillLevels.attack = {}; skillLevels.attack.skillName = "Attack"; skillLevels.attack.currentLevel = skill.level; skillLevels.attack.currentExp = skill.experience; } else if (skill.skillHrid.includes("power")) { skillLevels.power = {}; skillLevels.power.skillName = "Power"; skillLevels.power.currentLevel = skill.level; skillLevels.power.currentExp = skill.experience; } else if (skill.skillHrid.includes("defense")) { skillLevels.defense = {}; skillLevels.defense.skillName = "Defense"; skillLevels.defense.currentLevel = skill.level; skillLevels.defense.currentExp = skill.experience; } else if (skill.skillHrid.includes("ranged")) { skillLevels.ranged = {}; skillLevels.ranged.skillName = "Ranged"; skillLevels.ranged.currentLevel = skill.level; skillLevels.ranged.currentExp = skill.experience; } else if (skill.skillHrid.includes("magic")) { skillLevels.magic = {}; skillLevels.magic.skillName = "Magic"; skillLevels.magic.currentLevel = skill.level; skillLevels.magic.currentExp = skill.experience; } } const skillNamesInOrder = ["stamina", "intelligence", "attack", "power", "defense", "ranged", "magic"]; let hTMLStr = ""; for (const skill of skillNamesInOrder) { hTMLStr += `
${skillLevels[skill].skillName}${ isZH ? "到" : " to level " }${isZH ? "级" : ""}
`; } hTMLStr += `
${ isZH ? "天后" : "days after" }
`; hTMLStr += `
`; hTMLStr += `
`; parentDiv.innerHTML = hTMLStr; for (const skill of skillNamesInOrder) { const skillDiv = parentDiv.querySelector(`div#${"inputDiv_" + skill}`); const skillInput = parentDiv.querySelector(`input#${"input_" + skill}`); skillInput.onchange = () => { calculateTill(skill, skillInput, skillLevels, parentDiv, perHourGainExp); }; skillInput.addEventListener("keyup", function (evt) { calculateTill(skill, skillInput, skillLevels, parentDiv, perHourGainExp); }); skillDiv.onclick = () => { calculateTill(skill, skillInput, skillLevels, parentDiv, perHourGainExp); }; } const daysAfterDiv = parentDiv.querySelector(`div#script_afterDays`); const daysAfterInput = parentDiv.querySelector(`input#script_afterDays_input`); daysAfterInput.onchange = () => { calculateAfterDays(daysAfterInput, skillLevels, parentDiv, perHourGainExp, skillNamesInOrder); }; daysAfterInput.addEventListener("keyup", function (evt) { calculateAfterDays(daysAfterInput, skillLevels, parentDiv, perHourGainExp, skillNamesInOrder); }); daysAfterDiv.onclick = () => { calculateAfterDays(daysAfterInput, skillLevels, parentDiv, perHourGainExp, skillNamesInOrder); }; // 提取成本和收益 const expensesSpan = document.querySelector(`span#expensesSpan`); const revenueSpan = document.querySelector(`span#revenueSpan`); const profitSpan = document.querySelector(`span#profitPreview`); const expenseDiv = document.querySelector(`div#script_expense`); const revenueDiv = document.querySelector(`div#script_revenue`); if (expenseDiv && expenseDiv) { expenseDiv.textContent = expensesSpan.parentNode.textContent; revenueDiv.textContent = revenueSpan.parentNode.textContent; } else { profitSpan.parentNode.insertAdjacentHTML( "beforeend", `
${expensesSpan.parentNode.textContent}
${revenueSpan.parentNode.textContent}
` ); } } function calculateAfterDays(daysAfterInput, skillLevels, parentDiv, perHourGainExp, skillNamesInOrder) { const initData_levelExperienceTable = JSON.parse(GM_getValue("init_client_data")).levelExperienceTable; const days = Number(daysAfterInput.value); parentDiv.querySelector(`div#needDiv`).textContent = `${isZH ? "" : "After"} ${days} ${isZH ? "天后:" : "days: "}`; const listDiv = parentDiv.querySelector(`div#needListDiv`); let html = ""; let resultLevels = {}; for (const skillName of skillNamesInOrder) { for (const skill of Object.values(skillLevels)) { if (skill.skillName.toLowerCase() === skillName.toLowerCase()) { const exp = skill.currentExp + perHourGainExp[skill.skillName.toLowerCase()] * days * 24; let level = 1; while (initData_levelExperienceTable[level] < exp) { level++; } level--; const minExpAtLevel = initData_levelExperienceTable[level]; const maxExpAtLevel = initData_levelExperienceTable[level + 1] - 1; const expSpanInLevel = maxExpAtLevel - minExpAtLevel; const levelPercentage = Number(((exp - minExpAtLevel) / expSpanInLevel) * 100).toFixed(1); resultLevels[skillName.toLowerCase()] = level; html += `
${skill.skillName} ${isZH ? "" : "level"} ${level} ${isZH ? "级" : ""} ${levelPercentage}%
`; break; } } } const combatLevel = 0.2 * (resultLevels.stamina + resultLevels.intelligence + resultLevels.defense) + 0.4 * Math.max(0.5 * (resultLevels.attack + resultLevels.power), resultLevels.ranged, resultLevels.magic); html += `
${isZH ? "战斗等级:" : "Combat level: "} ${combatLevel.toFixed(1)}
`; listDiv.innerHTML = html; } function calculateTill(skillName, skillInputElem, skillLevels, parentDiv, perHourGainExp) { const initData_levelExperienceTable = JSON.parse(GM_getValue("init_client_data")).levelExperienceTable; const targetLevel = Number(skillInputElem.value); parentDiv.querySelector(`div#needDiv`).textContent = `${skillLevels[skillName].skillName} ${isZH ? "到" : "to level"} ${targetLevel} ${ isZH ? "级 还需:" : " takes: " }`; const listDiv = parentDiv.querySelector(`div#needListDiv`); const currentLevel = Number(skillLevels[skillName].currentLevel); const currentExp = Number(skillLevels[skillName].currentExp); if (targetLevel > currentLevel && targetLevel <= 200) { if (perHourGainExp[skillName] === 0) { listDiv.innerHTML = isZH ? "永远" : "Forever"; } else { let needExp = initData_levelExperienceTable[targetLevel] - currentExp; let needHours = needExp / perHourGainExp[skillName]; let html = ""; html += `
[${hoursToReadableString(needHours)}]
`; const consumeDivs = document.querySelectorAll(`div#simulationResultConsumablesUsed div.row`); for (const elem of consumeDivs) { const conName = elem.children[0].textContent; const conPerHour = Number(elem.children[1].textContent); html += `
${conName} ${Number(conPerHour * needHours).toFixed(0)}
`; } listDiv.innerHTML = html; } } else { listDiv.innerHTML = isZH ? "输入错误" : "Input error"; } } function hoursToReadableString(hours) { const sec = hours * 60 * 60; if (sec >= 86400) { return Number(sec / 86400).toFixed(1) + (isZH ? " 天" : " days"); } const d = new Date(Math.round(sec * 1000)); function pad(i) { return ("0" + i).slice(-2); } let str = d.getUTCHours() + "h " + pad(d.getUTCMinutes()) + "m " + pad(d.getUTCSeconds()) + "s"; return str; } })();