您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
战斗模拟辅助工具,实时监听角色配置变化,导入当前角色实时配置
- // ==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.
- //
- // https://gf.qytechs.cn/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();
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址