您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
战斗模拟辅助工具,实时监听角色配置变化,导入当前角色实时配置
当前为
// ==UserScript== // @name [MWI] Realtime Import Of Battle Simulation // @name:zh-CN [银河奶牛]战斗模拟实时导入 // @namespace http://tampermonkey.net/ // @version 0.2.5 // @description Battle simulation imports the realtime configuration of the current character. // @description:zh-CN 战斗模拟辅助工具,实时监听角色配置变化,导入当前角色实时配置 // @icon https://www.milkywayidle.com/favicon.svg // @author Yannis // @license CC-BY-NC-SA-4.0 // @match https://www.milkywayidle.com/* // @match https://test.milkywayidle.com/* // @match https://*/MWICombatSimulatorTest/dist/* // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @connect textdb.online // @require https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js // ==/UserScript== // 感谢 'MWITool' 为本脚本提供的技术参考,本脚本部分代码来源于 MWITool,请勿删除本版权声明 // 本脚本若有任何问题,欢迎随时与开发者联系与反馈,感谢使用 // Thanks 'MWITool' for the technical reference provided for this script. // Some of the code in this script is sourced from MWITool. // Please do not delete this copyright notice. // // http://greasyfork.icu/en/scripts/494467-mwitools (function () { 'use strict'; const debug = console.log.bind(null, '%c[BatSync]%c', 'color:green', 'color:black'); const info = console.log.bind(null, '%c[BatSync]%c', 'color:cyan', 'color:black'); const error = console.log.bind(null, '%c[BatSync]%c', 'color:red', 'color:black'); // 语言设定 const isZHInGameSetting = localStorage.getItem("i18nextLng")?.toLowerCase()?.startsWith("zh"); let isZH = isZHInGameSetting; let playerId; let firstImport = true; let clientData = {}; // #region TextDB // 从TextDB获取数据 async function getDataFromTextDB(key) { // info(`Get data from TextDB: ${key}`); const response = await new Promise((resolve) => { GM_xmlhttpRequest({ method: 'GET', url: `https://textdb.online/${key}`, timeout: 5000, onload: resolve, ontimeout: (e) => resolve({ status: 504, error: "timeout" }), onerror: (e) => resolve({ status: 500, error: e }) }) }); if (response.status !== 200) { error(`Error get from TextDB`, { key: key, status: response.status, error: response.error }); } else { info(`Get data from TextDB`, { key: key, data: response.responseText }); } return response.responseText; } // 保存数据到TextDB async function saveDataToTextDB(key, data) { // info("保存TextDB数据", { // key: key, // data: data // }); const params = new URLSearchParams(); params.append('key', key); params.append('value', data.toString()); const response = await new Promise((resolve) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://api.textdb.online/update/', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, data: params, onload: resolve, onerror: function (e) { error("Error saving to TextDB:", e); reject(e); } }); }); if (response.status !== 200) { error('Failed saving to TextDB:', response); } else { info(`Save data to TextDB success, key: ${key}`) } } // 生成玩家唯一Key(MD5) function getPlayerUniqueKey(characterId) { return `mwi_${characterId}_${md5(md5(characterId))}`; } // #endregion // #region 角色数据 // 获取客户端初始化数据 function getInitClientData() { return JSON.parse(GM_getValue("init_client_data", "")); } // 获取当前角色数据 function getCurrentPlayerData() { let playerId = GM_getValue("current_character_id", null); if (playerId) { return getPlayerData(playerId); } else { return; } } // 获取角色数据 function getPlayerData(id) { let playersDataStr = GM_getValue("mwi_players_data", null) || JSON.stringify(new Array()); let playersData = JSON.parse(playersDataStr); const pIndex = playersData.findIndex(obj => obj.character.id === id); if (pIndex !== -1) { return playersData[pIndex]; } else { return; } } // 保存角色数据 function saveCharacterData(obj) { let playersDataStr = GM_getValue("mwi_players_data", null) || JSON.stringify(new Array()); let playersData = JSON.parse(playersDataStr); playersData = playersData.filter(e => e.character.id !== obj.character.id); playersData.unshift(obj); if (playersData.length > 20) { playersData.pop(); } GM_setValue("mwi_players_data", JSON.stringify(playersData)); } // #endregion // #region HookMessage // 监听WebSocket 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 && socket.url.indexOf("api-test.milkywayidle.com/ws") <= -1) { return oriGet.call(this); } const message = oriGet.call(this); Object.defineProperty(this, "data", { value: message }); // Anti-loop try { handleMessage(message); } catch (e) { error(`处理消息协议时出错: ${e}`); console.log(e.stack); } return message; } } // 消息处理 function handleMessage(message) { let obj = JSON.parse(message); if (!obj) { return; } switch (obj.type) { case 'pong': { // ping-pong break; } case 'active_player_count_updated': { // 活跃人数更新 break; } case 'init_client_data': { // 客户端数据 GM_setValue("init_client_data", message); clientData.actionDetailMap = obj.actionDetailMap; clientData.levelExperienceTable = obj.levelExperienceTable; clientData.itemDetailMap = obj.itemDetailMap; clientData.actionCategoryDetailMap = obj.actionCategoryDetailMap; clientData.abilityDetailMap = obj.abilityDetailMap; break; } case 'init_character_data': { playerId = obj.character.id; // 初始化信息 GM_setValue("init_character_data", message); GM_setValue("current_character_id", playerId); let player = getPlayerData(playerId); if (player) { obj.abilityCombatTriggersMap = { ...player.abilityCombatTriggersMap, ...obj.abilityCombatTriggersMap } obj.consumableCombatTriggersMap = { ...player.consumableCombatTriggersMap, ...obj.consumableCombatTriggersMap } } obj.battleObj = buildBattleObjFromPlayer(obj, true); saveCharacterData(obj); saveDataToTextDB(getPlayerUniqueKey(playerId), JSON.stringify(obj.battleObj)); break; } case 'profile_shared': { // 角色详情 let player = getPlayerData(obj.profile.characterSkills[0].characterID) let battleObj = buildBattleObjFromProfileShared(player, obj); if (!player) { // 不是本角色 player = {} player.character = {} player.character.id = battleObj.character.id player.character.name = battleObj.character.name } player.battleObj = battleObj; saveCharacterData(player); let playerUniqueKey = getPlayerUniqueKey(player.character.id); info(`Player Uniquekey: `, { playerId: player.character.id, playerName: player.character.name, playerUniqueKey: playerUniqueKey, textDBUrl: `https://textdb.online/${playerUniqueKey}` }); addExportButton(player.character.id); break; } case 'new_battle': { // 战斗更新 for (const battlePlayer of obj.players) { let player = getPlayerData(battlePlayer.character.id); let battleObj = buildBattleObjFromNewBattle(player, battlePlayer); if (!player) { // 不是本角色 player = {} player.character = {} player.character.id = battleObj.character.id player.character.name = battleObj.character.name } player.battleObj = battleObj; saveCharacterData(player); } break; } case 'items_updated': { // 物品更新 let player = getPlayerData(playerId); if (!player) { break; } let update = false; if (obj.endCharacterItems) { for (const item of Object.values(obj.endCharacterItems)) { if (item.itemLocationHrid !== "/item_locations/inventory" && item.count > 0) { // 装备更新 let equipment = player.battleObj.player.equipment; equipment = equipment.filter(e => e.itemLocationHrid !== item.itemLocationHrid); equipment.push({ itemLocationHrid: item.itemLocationHrid, itemHrid: item.itemHrid, enhancementLevel: item.enhancementLevel, }) player.battleObj.player.equipment = equipment; update = true; } } } if (update) { saveCharacterData(player); } break; } case 'action_type_consumable_slots_updated': { // 消耗栏更新 let player = getPlayerData(playerId); if (!player) { break; } player.actionTypeDrinkSlotsMap = obj.actionTypeDrinkSlotsMap; player.actionTypeFoodSlotsMap = obj.actionTypeFoodSlotsMap; player.battleObj = buildBattleObjFromPlayer(player, false); saveCharacterData(player); break; } case 'abilities_updated': { // 技能更新 let player = getPlayerData(playerId); let equippedAbilities = JSON.parse(JSON.stringify(player.combatUnit.combatAbilities)); for (let i = equippedAbilities.length; i < 5; i++) { equippedAbilities.push({}) } if (obj.endCharacterAbilities) { for (const ability of obj.endCharacterAbilities) { // 更新技能详情 const aDetail = player.characterAbilities.find(e => e.abilityHrid === ability.abilityHrid); if (aDetail) { aDetail.slotNumber = ability.slotNumber; } // 更新已装备技能信息 const aIndex = equippedAbilities.findIndex(e => e.abilityHrid === ability.abilityHrid); if (aIndex >= 0) { equippedAbilities[aIndex] = {} } if (ability.slotNumber > 0) { equippedAbilities.splice(ability.slotNumber - 1, 0, { abilityHrid: ability.abilityHrid, level: ability.level, experience: ability.experience, availableTime: ability.updatedAt }) } } } player.combatUnit.combatAbilities = equippedAbilities.filter(e => e.abilityHrid && e.abilityHrid.length > 0); player.battleObj = buildBattleObjFromPlayer(player, false); saveCharacterData(player); break; } case 'combat_triggers_updated': { let player = getPlayerData(playerId); if (!player) { break; } if (obj.combatTriggerTypeHrid === '/combat_trigger_types/ability') { // 技能栏 Trigger 更新 player.abilityCombatTriggersMap[obj.abilityHrid] = obj.combatTriggers; } else if (obj.combatTriggerTypeHrid === '/combat_trigger_types/consumable') { // 消耗栏 Trigger 更新 player.consumableCombatTriggersMap[obj.itemHrid] = obj.combatTriggers; } else { break; } player.battleObj = buildBattleObjFromPlayer(player, false); saveCharacterData(player); saveDataToTextDB(getPlayerUniqueKey(playerId), JSON.stringify(player.battleObj)); break; } case 'all_combat_triggers_updated': { // 所有 Triggers 更新 let player = getPlayerData(playerId); if (!player) { break; } player.abilityCombatTriggersMap = { ...player.abilityCombatTriggersMap, ...obj.abilityCombatTriggersMap }; player.consumableCombatTriggersMap = { ...player.consumableCombatTriggersMap, ...obj.consumableCombatTriggersMap }; player.battleObj = buildBattleObjFromPlayer(player, false); saveCharacterData(player); saveDataToTextDB(getPlayerUniqueKey(playerId), JSON.stringify(player.battleObj)); break; } case 'party_updated': { // 队伍更新 let player = getPlayerData(playerId); if (!player) { break; } player.partyInfo = obj.partyInfo; saveCharacterData(player); break; } case 'chat_message_received': { // 聊天消息 break; } case 'action_completed': { // 行动完成 break; } default: { // info(obj); } } } // #endregion // #region Builders // 构建战斗模拟信息(InitData) function buildBattleObjFromPlayer(obj, init) { let battleObj = init ? {} : obj.battleObj; // Base battleObj.character = {} battleObj.character.id = obj.character.id; battleObj.character.name = obj.character.name; battleObj.character.gameMode = obj.character.gameMode; battleObj.timestamp = Date.now(); battleObj.valid = true; if (init) { // Levels battleObj.player = {} for (const skill of obj.characterSkills) { if (skill.skillHrid.includes("stamina")) { battleObj.player.staminaLevel = skill.level; } else if (skill.skillHrid.includes("intelligence")) { battleObj.player.intelligenceLevel = skill.level; } else if (skill.skillHrid.includes("attack")) { battleObj.player.attackLevel = skill.level; } else if (skill.skillHrid.includes("power")) { battleObj.player.powerLevel = skill.level; } else if (skill.skillHrid.includes("defense")) { battleObj.player.defenseLevel = skill.level; } else if (skill.skillHrid.includes("ranged")) { battleObj.player.rangedLevel = skill.level; } else if (skill.skillHrid.includes("magic")) { battleObj.player.magicLevel = skill.level; } } // Equipments battleObj.player.equipment = []; if (obj.characterItems) { for (const item of obj.characterItems) { if (!item.itemLocationHrid.includes("/item_locations/inventory")) { battleObj.player.equipment.push({ itemLocationHrid: item.itemLocationHrid, itemHrid: item.itemHrid, enhancementLevel: item.enhancementLevel, }); } } } } // Food battleObj.food = {} battleObj.food["/action_types/combat"] = []; if (obj.actionTypeFoodSlotsMap["/action_types/combat"]) { for (const food of obj.actionTypeFoodSlotsMap["/action_types/combat"]) { if (food) { battleObj.food["/action_types/combat"].push({ itemHrid: food.itemHrid, }); } else { battleObj.food["/action_types/combat"].push({ itemHrid: "", }); } } } // Drinks battleObj.drinks = {} battleObj.drinks["/action_types/combat"] = []; if (obj.actionTypeDrinkSlotsMap["/action_types/combat"]) { for (const drink of obj.actionTypeDrinkSlotsMap["/action_types/combat"]) { if (drink) { battleObj.drinks["/action_types/combat"].push({ itemHrid: drink.itemHrid, }); } else { battleObj.drinks["/action_types/combat"].push({ itemHrid: "", }); } } } // Abilities battleObj.abilities = []; for (let i = 0; i < 5; i++) { battleObj.abilities.push({ abilityHrid: "", level: "1", }) } if (obj.combatUnit.combatAbilities) { for (const ability of obj.combatUnit.combatAbilities) { const aDetail = obj.characterAbilities.find(e => e.abilityHrid === ability.abilityHrid); if (aDetail) { battleObj.abilities[aDetail.slotNumber - 1] = { abilityHrid: ability.abilityHrid, level: ability.level, experience: ability.experience, availableTime: ability.updatedAt }; } } } // TriggerMap battleObj.triggerMap = { ...obj.abilityCombatTriggersMap, ...obj.consumableCombatTriggersMap }; // HouseRooms battleObj.houseRooms = {}; if (obj.characterHouseRoomMap) { for (const house of Object.values(obj.characterHouseRoomMap)) { battleObj.houseRooms[house.houseRoomHrid] = house.level; } } return battleObj; } // 构建战斗模拟信息(ProfileShared) function buildBattleObjFromProfileShared(player, obj) { let battleObj = {}; // Base battleObj.character = {} battleObj.character.id = player ? player.character.id : obj.profile.characterSkills[0].characterID; battleObj.character.name = obj.profile.sharableCharacter.name; battleObj.character.gameMode = obj.profile.sharableCharacter.gameMode; battleObj.timestamp = Date.now(); battleObj.valid = true; // Levels battleObj.player = {} for (const skill of obj.profile.characterSkills) { if (skill.skillHrid.includes("stamina")) { battleObj.player.staminaLevel = skill.level; } else if (skill.skillHrid.includes("intelligence")) { battleObj.player.intelligenceLevel = skill.level; } else if (skill.skillHrid.includes("attack")) { battleObj.player.attackLevel = skill.level; } else if (skill.skillHrid.includes("power")) { battleObj.player.powerLevel = skill.level; } else if (skill.skillHrid.includes("defense")) { battleObj.player.defenseLevel = skill.level; } else if (skill.skillHrid.includes("ranged")) { battleObj.player.rangedLevel = skill.level; } else if (skill.skillHrid.includes("magic")) { battleObj.player.magicLevel = skill.level; } } // Equipments battleObj.player.equipment = []; if (obj.profile.wearableItemMap) { for (const key in obj.profile.wearableItemMap) { const item = obj.profile.wearableItemMap[key]; battleObj.player.equipment.push({ itemLocationHrid: item.itemLocationHrid, itemHrid: item.itemHrid, enhancementLevel: item.enhancementLevel, }); } } // Food and Drinks battleObj.food = {} battleObj.food["/action_types/combat"] = []; battleObj.drinks = {} battleObj.drinks["/action_types/combat"] = []; let wearableItemMap = obj.profile.wearableItemMap; let weapon = null; if (wearableItemMap) { weapon = wearableItemMap["/item_locations/main_hand"]?.itemHrid || wearableItemMap["/item_locations/two_hand"]?.itemHrid; } if (player) { battleObj.food = player.battleObj.food; battleObj.drinks = player.battleObj.drinks; } else if (weapon) { if (weapon.includes("shooter") || weapon.includes("bow")) { // 远程 battleObj.food["/action_types/combat"] = [ // 2红1蓝 { itemHrid: "/items/spaceberry_donut" }, { itemHrid: "/items/spaceberry_cake" }, { itemHrid: "/items/star_fruit_yogurt" } ] battleObj.drinks["/action_types/combat"] = [ // 经验.超远.暴击 { itemHrid: "/items/wisdom_coffee" }, { itemHrid: "/items/super_ranged_coffee" }, { itemHrid: "/items/critical_coffee" } ] } else if (weapon.includes("boomstick") || weapon.includes("staff") || weapon.includes("trident")) { // 法师 battleObj.food["/action_types/combat"] = [ // 1红2蓝 { itemHrid: "/items/spaceberry_cake" }, { itemHrid: "/items/star_fruit_gummy" }, { itemHrid: "/items/star_fruit_yogurt" } ] battleObj.drinks["/action_types/combat"] = [ // 经验.超魔.吟唱 { itemHrid: "/items/wisdom_coffee" }, { itemHrid: "/items/super_magic_coffee" }, { itemHrid: "/items/channeling_coffee" } ] } else if (weapon.includes("bulwark")) { // 双手盾 battleObj.food["/action_types/combat"] = [ // 2红1蓝 { itemHrid: "/items/spaceberry_donut" }, { itemHrid: "/items/spaceberry_cake" }, { itemHrid: "/items/star_fruit_yogurt" } ] battleObj.drinks["/action_types/combat"] = [ // 经验.超防.超耐 { itemHrid: "/items/wisdom_coffee" }, { itemHrid: "/items/super_defense_coffee" }, { itemHrid: "/items/super_stamina_coffee" } ] } else { // 近战 battleObj.food["/action_types/combat"] = [ // 2红1蓝 { itemHrid: "/items/spaceberry_donut" }, { itemHrid: "/items/spaceberry_cake" }, { itemHrid: "/items/star_fruit_yogurt" } ] battleObj.drinks["/action_types/combat"] = [ // 经验.超力.迅捷 { itemHrid: "/items/wisdom_coffee" }, { itemHrid: "/items/super_power_coffee" }, { itemHrid: "/items/swiftness_coffee" } ] } } // Abilities battleObj.abilities = []; for (let i = 0; i < 5; i++) { battleObj.abilities.push({ abilityHrid: "", level: "1", }) } if (obj.profile.equippedAbilities) { let index = 1; for (const ability of obj.profile.equippedAbilities) { if (ability && clientData.abilityDetailMap[ability.abilityHrid].isSpecialAbility) { battleObj.abilities[0] = { abilityHrid: ability.abilityHrid, level: ability.level, experience: ability.experience, availableTime: ability.updatedAt }; } else if (ability) { battleObj.abilities[index++] = { abilityHrid: ability.abilityHrid, level: ability.level, experience: ability.experience, availableTime: ability.updatedAt }; } } } // TriggerMap if (player) { battleObj.triggerMap = player.battleObj.triggerMap; } // HouseRooms battleObj.houseRooms = {}; for (const house of Object.values(obj.profile.characterHouseRoomMap)) { battleObj.houseRooms[house.houseRoomHrid] = house.level; } return battleObj; } // 构建战斗模拟信息(NewBattle) function buildBattleObjFromNewBattle(player, obj) { let battleObj = {}; if (player) { battleObj = player.battleObj; } // Base battleObj.character = battleObj.character ?? {}; battleObj.character.id = obj.character.id; battleObj.character.name = obj.character.name; battleObj.character.gameMode = obj.character.gameMode; battleObj.timestamp = Date.now(); battleObj.valid = battleObj.valid; // Levels battleObj.player = battleObj.player ?? {}; battleObj.player.staminaLevel = battleObj.player.staminaLevel ?? 1; battleObj.player.intelligenceLevel = battleObj.player.intelligenceLevel ?? 1; battleObj.player.attackLevel = battleObj.player.attackLevel ?? 1; battleObj.player.powerLevel = battleObj.player.powerLevel ?? 1; battleObj.player.defenseLevel = battleObj.player.defenseLevel ?? 1; battleObj.player.rangedLevel = battleObj.player.rangedLevel ?? 1; battleObj.player.magicLevel = battleObj.player.magicLevel ?? 1; // Equipments battleObj.player.equipment = battleObj.player.equipment ?? []; // Food and Drinks battleObj.food = {}; battleObj.food["/action_types/combat"] = []; battleObj.drinks = {}; battleObj.drinks["/action_types/combat"] = []; if (obj.combatConsumables) { for (const consumable of obj.combatConsumables) { if (consumable.itemHrid.includes("coffee")) { battleObj.drinks["/action_types/combat"].push({ itemHrid: consumable.itemHrid }) } else { battleObj.food["/action_types/combat"].push({ itemHrid: consumable.itemHrid }) } } } // Abilities battleObj.abilities = []; for (let i = 0; i < 5; i++) { battleObj.abilities.push({ abilityHrid: "", level: "1", }) } if (obj.combatAbilities) { let index = 1; for (const ability of obj.combatAbilities) { if (ability && clientData.abilityDetailMap[ability.abilityHrid].isSpecialAbility) { battleObj.abilities[0] = { abilityHrid: ability.abilityHrid, level: ability.level, experience: ability.experience, availableTime: ability.updatedAt }; } else if (ability) { battleObj.abilities[index++] = { abilityHrid: ability.abilityHrid, level: ability.level, experience: ability.experience, availableTime: ability.updatedAt }; } } } // TriggerMap battleObj.triggerMap = { ...battleObj.triggerMap }; // HouseRooms battleObj.houseRooms = { ...battleObj.houseRooms }; return battleObj; } // #endregion // #region Battle Simulater // 添加个人资料导出 function addExportButton(characterId) { const checkElem = () => { const selectedElement = document.querySelector(`div.SharableProfile_overviewTab__W4dCV`); if (selectedElement) { clearInterval(timer); const button = document.createElement("button"); selectedElement.appendChild(button); button.textContent = isZH ? "查看云模拟数据" : "View Cloud Data"; button.style.borderRadius = "5px"; button.style.height = "30px"; button.style.backgroundColor = "orange"; button.style.color = "black"; button.style.boxShadow = "none"; button.style.border = "0px"; button.onclick = function () { window.open(`https://textdb.online/${getPlayerUniqueKey(characterId)}`) return false; }; return false; } }; let timer = setInterval(checkElem, 200); } // 添加实时导入按钮 function addImportButtonForMWICombatSimulate() { const checkElem = () => { const btnEquipSets = document.querySelector(`button#buttonEquipmentSets`); if (btnEquipSets) { clearInterval(timer); let divRow = document.createElement("div"); divRow.className = "row"; btnEquipSets.parentElement.parentElement.prepend(divRow); // 导入按钮 let div1 = document.createElement("div"); div1.className = "mb-3 pt-2"; divRow.append(div1); let button1 = document.createElement("button"); div1.append(button1); button1.textContent = isZH ? "实时导入本地数据" : "Real-time Import From Local"; button1.className = "btn btn-warning"; button1.onclick = function () { const btnGetPrice = document.querySelector(`button#buttonGetPrices`); if (btnGetPrice) { btnGetPrice.click(); } importDataForMWICombatSimulate(button1, false); return false; }; // 网络导入按钮 let div2 = document.createElement("div"); div2.className = "mb-3 pt-1"; divRow.append(div2); let button2 = document.createElement("button"); div2.append(button2); button2.textContent = isZH ? "实时导入网络云数据" : "Real-time Import From Network"; button2.className = "btn btn-warning"; button2.onclick = function () { const btnGetPrice = document.querySelector(`button#buttonGetPrices`); if (btnGetPrice) { btnGetPrice.click(); } importDataForMWICombatSimulate(button2, true); return false; }; } }; let timer = setInterval(checkElem, 200); } // 导入数据 async function importDataForMWICombatSimulate(button, readCloudData = false) { let resetZone = !firstImport; if (!firstImport) { let userConfirm = window.confirm(isZH ? "是否要覆盖当前数据" : "Do you want to overwrite the current data?"); if (!userConfirm) { return; } firstImport = false; } let preTextContent = button.textContent; let preClassName = button.className; button.textContent = isZH ? "正在导入数据..." : "Importing..."; button.className = "btn btn-warning"; button.disabled = true; clientData = getInitClientData(); let player = getCurrentPlayerData(); const BLANK_PLAYER_JSON_STR = `{\"player\":{\"attackLevel\":1,\"magicLevel\":1,\"powerLevel\":1,\"rangedLevel\":1,\"defenseLevel\":1,\"staminaLevel\":1,\"intelligenceLevel\":1,\"equipment\":[]},\"food\":{\"/action_types/combat\":[{\"itemHrid\":\"\"},{\"itemHrid\":\"\"},{\"itemHrid\":\"\"}]},\"drinks\":{\"/action_types/combat\":[{\"itemHrid\":\"\"},{\"itemHrid\":\"\"},{\"itemHrid\":\"\"}]},\"abilities\":[{\"abilityHrid\":\"\",\"level\":\"1\"},{\"abilityHrid\":\"\",\"level\":\"1\"},{\"abilityHrid\":\"\",\"level\":\"1\"},{\"abilityHrid\":\"\",\"level\":\"1\"},{\"abilityHrid\":\"\",\"level\":\"1\"}],\"triggerMap\":{},\"zone\":\"/actions/combat/fly\",\"simulationTime\":\"100\",\"houseRooms\":{\"/house_rooms/dairy_barn\":0,\"/house_rooms/garden\":0,\"/house_rooms/log_shed\":0,\"/house_rooms/forge\":0,\"/house_rooms/workshop\":0,\"/house_rooms/sewing_parlor\":0,\"/house_rooms/kitchen\":0,\"/house_rooms/brewery\":0,\"/house_rooms/laboratory\":0,\"/house_rooms/observatory\":0,\"/house_rooms/dining_room\":0,\"/house_rooms/library\":0,\"/house_rooms/dojo\":0,\"/house_rooms/gym\":0,\"/house_rooms/armory\":0,\"/house_rooms/archery_range\":0,\"/house_rooms/mystical_study\":0}}`; const players = {}; let isParty = false; let zone = "/actions/combat/fly"; let isZoneDungeon = false; if (!player?.partyInfo?.partySlotMap) { // 个人 players[1] = { name: player.character.name, imported: true, cloudData: false, battleData: JSON.stringify(player.battleObj), }; // Zone for (const action of player.characterActions) { if (action && action.actionHrid.includes("/actions/combat/")) { zone = action.actionHrid; isZoneDungeon = clientData.actionDetailMap[action.actionHrid]?.combatZoneInfo?.isDungeon; break; } } } else { // 队伍 isParty = true; let i = 0; for (const member of Object.values(player.partyInfo.partySlotMap)) { i++; if (member.characterID) { if (member.characterID === player.character.id) { players[i] = { name: player.character.name, imported: true, cloudData: false, battleData: JSON.stringify(player.battleObj), }; } else { let memberData = getPlayerData(member.characterID); let battleObj = memberData?.battleObj; if (readCloudData) { // 读取共享Trigger数据 let sharedTextDBStr = await getDataFromTextDB(getPlayerUniqueKey(member.characterID)); if (sharedTextDBStr) { let sharedTextDB = JSON.parse(sharedTextDBStr); if (battleObj) { battleObj.triggerMap = { ...battleObj.triggerMap, ...sharedTextDB.triggerMap } } else { battleObj = sharedTextDB; } } else { readCloudData = false; } } if (battleObj && battleObj.valid) { players[i] = { name: battleObj.character.name, imported: true, cloudData: readCloudData, battleData: JSON.stringify(battleObj), }; } else { players[i] = { name: isZH ? "需要点开个人资料" : "Open profile in game", imported: true, cloudData: false, battleData: BLANK_PLAYER_JSON_STR, }; } } } } // Zone zone = player.partyInfo?.party?.actionHrid; isZoneDungeon = clientData.actionDetailMap[zone]?.combatZoneInfo?.isDungeon; } // Select zone or dungeon if (zone) { document.querySelector(`input#simDungeonToggle`).checked = isZoneDungeon; document.querySelector(`input#simDungeonToggle`).dispatchEvent(new Event("change")); let elementZone = isZoneDungeon ? document.querySelector(`select#selectDungeon`) : document.querySelector(`select#selectZone`); if (elementZone.selectedIndex <= 0) { for (let i = 0; i < elementZone.options.length; i++) { if (elementZone.options[i].value === zone) { elementZone.options[i].selected = true; break; } } } } for (let i = 1; i <= 5; i++) { if (!players[i]) { players[i] = { name: `Player ${i}`, imported: false, cloudData: false, battleData: BLANK_PLAYER_JSON_STR, }; } let aTab = document.querySelector(`a#player${i}-tab`); aTab.textContent = players[i].name; aTab.style.cssText = '' if (players[i].cloudData) { aTab.style.backgroundImage = "linear-gradient(-20deg, #00cdac 0%, #8ddad5 100%)"; aTab.style.color = "black"; } let checkbox = document.querySelector(`input#player${i}.form-check-input.player-checkbox`); if (checkbox) { checkbox.checked = players[i].imported; checkbox.dispatchEvent(new Event("change")); } } document.querySelector(`a#group-combat-tab`).click(); const editImport = document.querySelector(`input#inputSetGroupCombatAll`); editImport.value = JSON.stringify(Object.keys(players).reduce((acc, key) => { acc[key] = players[key].battleData; return acc; }, {})); document.querySelector(`button#buttonImportSet`).click(); // 模拟时长 document.querySelector(`input#inputSimulationTime`).value = 24; button.textContent = isZH ? "成功导入数据" : "Imported Successful"; button.className = "btn btn-success"; button.disabled = false; setTimeout(() => { button.textContent = preTextContent; button.className = preClassName; }, 1500); if (!isParty) { setTimeout(() => { document.querySelector(`button#buttonStartSimulation`).click(); }, 500); } } // 监听模拟结果 async function observeResultsForMWICombatSimulate() { let resultDiv = document.querySelector(`div.row`)?.querySelectorAll(`div.col-md-5`)?.[2]?.querySelector(`div.row > div.col-md-5`); while (!resultDiv) { await new Promise((resolve) => setTimeout(resolve, 100)); resultDiv = document.querySelector(`div.row`)?.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); } // #endregion // ================================================== // Script Start // ================================================== if (localStorage.getItem("initClientData")) { const obj = JSON.parse(localStorage.getItem("initClientData")); GM_setValue("init_client_data", localStorage.getItem("initClientData")); clientData.actionDetailMap = obj.actionDetailMap; clientData.levelExperienceTable = obj.levelExperienceTable; clientData.itemDetailMap = obj.itemDetailMap; clientData.actionCategoryDetailMap = obj.actionCategoryDetailMap; clientData.abilityDetailMap = obj.abilityDetailMap; } if (document.URL.includes("/MWICombatSimulatorTest/dist")) { addImportButtonForMWICombatSimulate(); observeResultsForMWICombatSimulate(); } hookWS(); })();