Files
bettergi-scripts-list/repo/js/AutoDomainCustomizable/main.js
FFA f9ee9f4ce5 AutoDomainCustomizable - 0.10 (#3108)
* V0.10

* 调整战斗策略设置方式
* 在显示日志中新增说明
* 读我文件排版
* 调试模式新增显示传入参数

* 修正刷完樹脂模式

修正刷完樹脂模式的邏輯

* 修正設置文字

修正刷完模式的文字描述
2026-04-12 12:34:12 +08:00

302 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Version: 1.7
// Modified Date: 2026-04-11
(async function () {
// =========================================================================
// 0. 动态加载数据 (基于 Genshin_Domains_SC_Live_Source.json)
// =========================================================================
let MATERIAL_DB = {};
const DB_FILENAME = "Genshin_Domains_SC_Live_Source.json";
try {
let rawContent = file.ReadTextSync(DB_FILENAME);
if (!rawContent) throw new Error("读取文件返回空内容");
let sourceData = JSON.parse(rawContent);
if (!sourceData || !sourceData.domains) throw new Error("JSON文件格式不正确缺少 domains 字段");
// 转换角色天赋素材
if (sourceData.domains.talent_books) {
sourceData.domains.talent_books.forEach(item => {
let domain = item.domain_name_sc;
if (item.schedule.Mon_Thu) MATERIAL_DB[item.schedule.Mon_Thu] = { domain: domain, idx: 1 };
if (item.schedule.Tue_Fri) MATERIAL_DB[item.schedule.Tue_Fri] = { domain: domain, idx: 2 };
if (item.schedule.Wed_Sat) MATERIAL_DB[item.schedule.Wed_Sat] = { domain: domain, idx: 3 };
});
}
// 转换武器升级素材
if (sourceData.domains.weapon_materials) {
sourceData.domains.weapon_materials.forEach(item => {
let domain = item.domain_name_sc;
if (item.schedule.Mon_Thu) MATERIAL_DB[item.schedule.Mon_Thu] = { domain: domain, idx: 1 };
if (item.schedule.Tue_Fri) MATERIAL_DB[item.schedule.Tue_Fri] = { domain: domain, idx: 2 };
if (item.schedule.Wed_Sat) MATERIAL_DB[item.schedule.Wed_Sat] = { domain: domain, idx: 3 };
});
}
// 转换圣遗物
if (sourceData.domains.artifacts) {
sourceData.domains.artifacts.forEach(item => {
let domain = item.domain_name_sc;
let key = item.drops.join(" / ");
MATERIAL_DB[key] = { domain: domain, type: "artifact" };
});
}
log.info(`【系统】成功加载外部数据文件: ${DB_FILENAME},包含 ${Object.keys(MATERIAL_DB).length} 条记录`);
} catch (e) {
log.error(`【致命错误】无法读取或解析 ${DB_FILENAME}`);
log.error(`错误详情: ${e.message}`);
return;
}
// =========================================================================
// 1. 读取并验证用户设置 (全 input-text 防呆机制)
// =========================================================================
let userConfig = settings;
let enableDebug = userConfig.EnableDebug;
// --- 调试模式输出所有使用者设定参数 ---
if (enableDebug) {
log.info("[DEBUG] ====== 收到使用者设定参数 (User Config) ======");
for (let key in userConfig) {
log.info(`[DEBUG] ${key}: ${userConfig[key]}`);
}
log.info("[DEBUG] ==============================================");
}
let pForceRunMode = userConfig.ForceRunMode;
let pRunUntilDepleted = userConfig.RunUntilResinDepleted;
let pAutoArtifactSalvage = userConfig.AutoArtifactSalvage;
let pMaxArtifactStar = userConfig.MaxArtifactStar || "4";
// --- 1.1 秘境目标防呆 ---
let pTalent = userConfig.TargetTalentMaterialName;
let pWeapon = userConfig.TargetWeaponMaterialName;
let pArtifact = userConfig.TargetArtifactName;
let selectedCount = (pTalent ? 1 : 0) + (pWeapon ? 1 : 0) + (pArtifact ? 1 : 0);
if (selectedCount === 0) {
log.error("【配置错误】您未选择任何素材!请在天赋、武器或圣遗物中选择一项。");
return;
}
if (selectedCount > 1) {
log.error("【配置冲突】您同时选择了多种类型的素材!请只保留一项,将其余两项设为空。");
return;
}
let pTargetMaterial = pTalent || pWeapon || pArtifact;
// --- 1.2 队伍名称防呆 (input-text) ---
let pPartyName = (userConfig.PartyName || "").toString().trim();
if (pPartyName !== "") {
try {
new RegExp(pPartyName); // 测试正则表达式是否合法
} catch (e) {
log.warn(`【配置警告】队伍名称的正则表达式语法错误: ${pPartyName},已重置为空 (不执行队伍切换)。`);
pPartyName = "";
}
}
// --- 1.3 战斗策略防呆 (select + input-text) ---
let pCombatStrategyType = (userConfig.CombatStrategyType || "").toString().trim();
let pSpecifiedCombatStrategy = (userConfig.SpecifiedCombatStrategy || "").toString().trim();
let pCombatStrategyPath = "";
if (pCombatStrategyType === "指定战斗策略") {
if (!pSpecifiedCombatStrategy) {
log.warn("【配置警告】您选择了'指定战斗策略'但未填写策略名称,系统将自动退回'根据队伍自动选择'。");
} else if (/[\\/:*?"<>|]/.test(pSpecifiedCombatStrategy)) {
log.warn(`【配置警告】指定的战斗策略名称包含非法字元: ${pSpecifiedCombatStrategy},系统将自动退回'根据队伍自动选择'。`);
} else {
pCombatStrategyPath = pSpecifiedCombatStrategy;
}
}
// --- 1.4 各项数值与树脂防呆 (input-text 与 select) ---
let pOriginalAmount = parseInt(userConfig.OriginalResinAmount) || 0;
let pOriginal40 = Math.floor(pOriginalAmount / 40);
let pOriginal20 = Math.floor((pOriginalAmount % 40) / 20);
let pCondensed = parseInt(userConfig.CondensedResinUseCount) || 0;
let pTransient = parseInt(userConfig.TransientResinUseCount) || 0;
// 脆弱树脂 (input-text)
let pFragileRaw = parseInt(userConfig.FragileResinUseCount);
let pFragile = (isNaN(pFragileRaw) || pFragileRaw < 0) ? 0 : pFragileRaw;
if (userConfig.FragileResinUseCount && (isNaN(pFragileRaw) || pFragileRaw < 0)) {
log.warn("【配置警告】脆弱树脂刷取数量输入无效或为负数,已自动重置为 0。");
}
// 秘境轮数 (input-text)
let pDomainRoundNumRaw = parseInt(userConfig.DomainRoundNum);
let pDomainRoundNum = (isNaN(pDomainRoundNumRaw) || pDomainRoundNumRaw < 0) ? 0 : pDomainRoundNumRaw;
if (userConfig.DomainRoundNum && (isNaN(pDomainRoundNumRaw) || pDomainRoundNumRaw < 0)) {
log.warn("【配置警告】秘境刷取轮数输入无效,已自动重置为 0 (无限轮直至树脂耗尽)。");
}
// 战斗后等待时间 (input-text)
let pFightEndDelayRaw = parseInt(userConfig.FightEndDelay);
let pFightEndDelay = (isNaN(pFightEndDelayRaw) || pFightEndDelayRaw < 0) ? 5 : pFightEndDelayRaw;
if (userConfig.FightEndDelay && (isNaN(pFightEndDelayRaw) || pFightEndDelayRaw < 0)) {
log.warn("【配置警告】战斗完成后等待时间输入无效,已恢复默认值 5 秒。");
}
// 树脂刷取顺序 (input-text)
let rawOrderStr = (userConfig.ResinUsageOrder || "").toString().trim();
if (/[^12345]/.test(rawOrderStr)) {
log.warn(`【配置警告】树脂刷取顺序包含无效字符,非 1~5 的内容已被自动过滤。`);
}
let pOrderStr = [...new Set(rawOrderStr.replace(/[^12345]/g, '').split(''))].join('');
if (rawOrderStr.replace(/[^12345]/g, '').length !== pOrderStr.length) {
log.warn(`【配置警告】树脂刷取顺序中存在重复设定的数字,已自动去重处理为: ${pOrderStr}`);
}
let priorityList = [];
for (let char of pOrderStr) {
if (char === '1') priorityList.push("浓缩树脂");
else if (char === '2') priorityList.push("原粹树脂40");
else if (char === '3') priorityList.push("原粹树脂20");
else if (char === '4') priorityList.push("须臾树脂");
else if (char === '5') priorityList.push("脆弱树脂");
}
if (!pRunUntilDepleted && pOriginalAmount === 0 && pCondensed === 0 && pTransient === 0 && pFragile === 0 && pDomainRoundNum === 0) {
log.warn("【配置警告】您未设置任何树脂消耗数量,也未限制秘境轮数!脚本可能进本后无事可做。");
}
// =========================================================================
// 2. 智能逻辑处理 (日期检查)
// =========================================================================
const WEEK_MAP = ["日", "一", "二", "三", "四", "五", "六"];
let materialInfo = MATERIAL_DB[pTargetMaterial];
let pDomainName = "";
let pSundaySelectedValue = "";
if (!materialInfo) {
log.error(`【数据错误】无法在数据库中找到项目:${pTargetMaterial}`);
return;
}
if (materialInfo.type === 'artifact') {
pDomainName = materialInfo.domain;
log.info(`【圣遗物模式】目标:${pTargetMaterial}`);
} else {
let requiredIdx = materialInfo.idx;
pDomainName = materialInfo.domain;
pSundaySelectedValue = requiredIdx.toString();
let serverOffsetMs = ServerTime.GetServerTimeZoneOffset();
let utcNow = Date.now();
let logicTimeMs = utcNow + serverOffsetMs - (4 * 60 * 60 * 1000);
let dayOfWeek = new Date(logicTimeMs).getUTCDay();
let dayStr = WEEK_MAP[dayOfWeek];
if (enableDebug) {
log.info(`[DEBUG] 游戏服务器时间: ${new Date(utcNow + serverOffsetMs).toUTCString()}`);
}
let isDateOpen = (dayOfWeek === 0) ||
(requiredIdx === 1 && (dayOfWeek === 1 || dayOfWeek === 4)) ||
(requiredIdx === 2 && (dayOfWeek === 2 || dayOfWeek === 5)) ||
(requiredIdx === 3 && (dayOfWeek === 3 || dayOfWeek === 6));
log.info(`【智能匹配】目标: ${pTargetMaterial} (游戏内星期${dayStr})`);
if (!isDateOpen && !pForceRunMode) {
log.error(`【停止运行】今日非该素材开放日。若需强制运行,请在设置中勾选。`);
return;
}
}
// =========================================================================
// 3. 执行主流程
// =========================================================================
let taskParam = new AutoDomainParam(pDomainRoundNum);
taskParam.DomainName = pDomainName;
taskParam.PartyName = pPartyName;
taskParam.AutoArtifactSalvage = pAutoArtifactSalvage;
taskParam.MaxArtifactStar = pMaxArtifactStar;
taskParam.SundaySelectedValue = pSundaySelectedValue;
// 调用底层方法以支持路径转换,并将回传值賦予屬性
taskParam.CombatStrategyPath = taskParam.SetCombatStrategyPath(pCombatStrategyPath);
taskParam.SpecifyResinUse = !pRunUntilDepleted;
// 樹脂數量與警告邏輯
if (!pRunUntilDepleted) {
taskParam.OriginalResin20UseCount = pOriginal20;
taskParam.OriginalResin40UseCount = pOriginal40;
taskParam.OriginalResinUseCount = 0;
taskParam.CondensedResinUseCount = pCondensed;
taskParam.TransientResinUseCount = pTransient;
taskParam.FragileResinUseCount = pFragile;
if (pOrderStr === "") {
log.warn("【配置警告】树脂刷取顺序完全留空!在指定次数模式下,脚本将不会消耗任何树脂。");
} else {
if (pCondensed > 0 && !pOrderStr.includes('1')) log.warn("【配置警告】浓缩树脂数量大于0但未配置在刷取顺序(1)中,将被底层忽略!");
if (pOriginal40 > 0 && !pOrderStr.includes('2')) log.warn("【配置警告】原粹树脂需要消耗40体力但未配置在刷取顺序(2)中,将被底层忽略!");
if (pOriginal20 > 0 && !pOrderStr.includes('3')) log.warn("【配置警告】原粹树脂需要消耗20体力但未配置在刷取顺序(3)中,将被底层忽略!");
if (pTransient > 0 && !pOrderStr.includes('4')) log.warn("【配置警告】须臾树脂数量大于0但未配置在刷取顺序(4)中,将被底层忽略!");
if (pFragile > 0 && !pOrderStr.includes('5')) log.warn("【配置警告】脆弱树脂数量大于0但未配置在刷取顺序(5)中,将被底层忽略!");
}
} else {
if (pOrderStr === "") {
log.warn("【配置警告】树脂刷取顺序完全留空!在耗尽模式下,脚本将不会消耗任何树脂。");
}
}
// 統一由 priorityList 決定刷取順序 (包含耗盡模式)
if (priorityList.length > 0) {
taskParam.SetResinPriorityList(...priorityList);
} else {
taskParam.SetResinPriorityList("");
}
while (true) {
try {
await dispatcher.RunAutoDomainTask(taskParam);
if (pFightEndDelay > 0) {
log.info(`[脚本] 任务结束,等待 ${pFightEndDelay} 秒...`);
await sleep(pFightEndDelay * 1000);
}
break;
} catch (ex) {
let msg = ex.message || "";
// --- 战斗策略档案不存在的降级重试逻辑 ---
if (msg.includes("战斗脚本文件不存在")) {
if (taskParam.CombatStrategyPath && taskParam.CombatStrategyPath !== "") {
log.warn(`[脚本] 找不到指定的战斗策略档案!`);
log.warn(`[脚本] 已触发降级机制退回【根据队伍自动选择】模式3 秒后重新启动任务...`);
// 调用底层方法正确切换为目录路径,并将回传值賦予屬性
taskParam.CombatStrategyPath = taskParam.SetCombatStrategyPath("");
await sleep(3000);
continue;
} else {
log.error("[脚本] 自动匹配战斗脚本失败:请检查 User/AutoFight 目录下是否有 txt 脚本,且脚本内角色是否符合当前队伍。");
break;
}
}
if (msg.includes("背包物品已满") || msg.includes("未检测到秘境结束")) {
log.warn(`[脚本] 任务可能已完成,但检测到: ${msg}`);
break;
} else if (msg.includes("复苏")) {
log.warn(`[脚本] 角色死亡5秒后重试...`);
await sleep(5000);
continue;
} else if (msg.includes("TaskCanceledException")) {
log.info("[脚本] 任务已取消。");
break;
} else if (msg.includes("未找到对应的秘境")) {
log.error("请等待BetterGI本体更新支援新秘境");
throw ex;
} else {
log.error(`[脚本] 错误: ${msg}`);
throw ex;
}
}
}
})();