mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-16 03:33:25 +08:00
@@ -1,4 +1,3 @@
|
||||
|
||||
const folderA = 'assets/狗粮A线@Yang-z/';
|
||||
const folderB = 'assets/狗粮B线@Yang-z/';
|
||||
const folderE = 'assets/狗粮额外@Yang-z/';
|
||||
@@ -30,7 +29,7 @@ const pathingA = [
|
||||
"【收尾】狗粮-稻妻-神无冢-踏鞴砂①-6个/21个-f.json",
|
||||
"【收尾】狗粮-稻妻-神无冢-踏鞴砂②-7个/21个-f.json",
|
||||
"【收尾】狗粮-稻妻-神无冢-踏鞴砂③-8个/21个-f.json"
|
||||
]; // 98+21个
|
||||
];
|
||||
|
||||
const pathingB = [
|
||||
"狗粮-枫丹-枫丹庭区-3个.json",
|
||||
@@ -61,40 +60,38 @@ const pathingB = [
|
||||
"狗粮-稻妻-鹤观-南-2个-f.json",
|
||||
"(恢复)狗粮-稻妻-清籁岛.json",
|
||||
"【收尾】狗粮-稻妻-清籁岛-清籁丸-20个-f.json"
|
||||
]; // 97+20个
|
||||
];
|
||||
|
||||
const pathingE = [
|
||||
"【额外】狗粮-纳塔-鸡屁股+8个/9个-f.json", // 12小时刷新
|
||||
]; // 7个
|
||||
"【额外】狗粮-纳塔-鸡屁股+8个/9个-f.json",
|
||||
];
|
||||
|
||||
const pathingE_A = [
|
||||
"【额外】狗粮-须弥-水天丛林+7个-f.json", // 24小时刷新
|
||||
"【额外】狗粮-枫丹-研究院区-新枫丹科学院周边+3个-f.json", // 24小时刷新
|
||||
]; // 10个
|
||||
"【额外】狗粮-须弥-水天丛林+7个-f.json",
|
||||
"【额外】狗粮-枫丹-研究院区-新枫丹科学院周边+3个-f.json",
|
||||
];
|
||||
|
||||
const pathingE_B = [
|
||||
"【额外】狗粮-纳塔-灵谜纹+13个.json" // 24小时刷新
|
||||
]; // 13个
|
||||
"【额外】狗粮-纳塔-灵谜纹+13个.json"
|
||||
];
|
||||
|
||||
// 每日拾取点位数及耗时
|
||||
// A: (98 + 21) + (8 + 10) = 137 ~ 31 + 10 = 41 minutes
|
||||
// B: (97 + 20) + (8 + 13) = 138 ~ 32 + 11 = 43 minutes
|
||||
// 读取用户设置
|
||||
let path = settings.path != undefined ? settings.path : '';
|
||||
let swapPath = settings.swapPath != undefined && settings.swapPath != '否' ? true : false;
|
||||
let extra = settings.extra != undefined && settings.extra != '是' ? false : true;
|
||||
let extraAB = settings.extraAB != undefined && settings.extraAB != '是' ? false : true;
|
||||
let autoSalvage = settings.autoSalvage != undefined && settings.autoSalvage != '是' ? false : true;
|
||||
let autoSalvageSpan = settings.autoSalvageSpan != undefined && ~~settings.autoSalvageSpan > 0 ? ~~settings.autoSalvageSpan : 10;
|
||||
let activeRestore = settings.activeRestore != undefined && settings.activeRestore != '是' ? false : true;
|
||||
const activeProgress = settings.activeProgress != undefined && settings.activeProgress != '否' ? true : false;
|
||||
const notify = settings.notify || false;
|
||||
|
||||
|
||||
// 读取用户设置
|
||||
let path = settings.path != undefined ? settings.path : '';
|
||||
let swapPath = settings.swapPath != undefined && settings.swapPath != '否' ? true : false;
|
||||
let extra = settings.extra != undefined && settings.extra != '是' ? false : true;
|
||||
let extraAB = settings.extraAB != undefined && settings.extraAB != '是' ? false : true;
|
||||
let autoSalvage = settings.autoSalvage != undefined && settings.autoSalvage != '是' ? false : true;
|
||||
let autoSalvage4 = settings.autoSalvage4 != undefined && settings.autoSalvage4 != '否' ? true : false;
|
||||
let autoSalvageSpan = settings.autoSalvageSpan != undefined && ~~settings.autoSalvageSpan > 0 ? ~~settings.autoSalvageSpan : 10;
|
||||
let activeRestore = settings.activeRestore != undefined && settings.activeRestore != '是' ? false : true;
|
||||
const activeProgress = settings.activeProgress != undefined && settings.activeProgress != '否' ? true : false;
|
||||
|
||||
|
||||
log.debug(`path: ${path}; swapPath: ${swapPath}; extra: ${extra}; extraAB: ${extraAB}; autoSalvage: ${autoSalvage}; autoSalvage4: ${autoSalvage4}; autoSalvageSpan: ${autoSalvageSpan}; activeRestore: ${activeRestore};`);
|
||||
// await sleep(30000);
|
||||
// 分解记录变量
|
||||
let totalSalvageExp = 0; // 累计EXP(不含首次分解)
|
||||
const salvageRecords = [];
|
||||
const salvageLogFile = "狗粮分解日志.txt";
|
||||
let pendingPaths = []; // 仅包含已执行的路径
|
||||
let count = 0; // 计数已执行的非恢复路径
|
||||
|
||||
// 确定路径
|
||||
function determinePath() {
|
||||
@@ -104,252 +101,314 @@ function determinePath() {
|
||||
const delta = now - benchmark;
|
||||
const days = delta / (1000 * 60 * 60 * 24);
|
||||
path = days % 2 < 1 ? 'A' : 'B';
|
||||
|
||||
if (swapPath) path = path == 'A' ? 'B' : 'A';
|
||||
}
|
||||
|
||||
// 如果路径切换,清理与当前路径无关的任务记录
|
||||
if (progress.path !== path) {
|
||||
progress.completedTasks = [];
|
||||
progress.path = path;
|
||||
saveProgress(); // 保存新的路径和清理后的任务记录
|
||||
saveProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 初始化函数
|
||||
async function init(shouldRestore = true, shouldResizeMap = false) {
|
||||
// 关闭强制交互
|
||||
dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false }));
|
||||
|
||||
// 恢复和对齐
|
||||
if (shouldRestore) {
|
||||
await genshin.tp("1468.0732421875", "1998.04443359375");
|
||||
await genshin.tp("-257.8800964", "627.0402832");
|
||||
await sleep(3000);
|
||||
}
|
||||
|
||||
// 调整地图缩放 (bgi[v0.41.0]后不需要)
|
||||
if (shouldResizeMap) {
|
||||
await resizeMap();
|
||||
}
|
||||
}
|
||||
|
||||
// 调整地图
|
||||
async function resizeMap(level = 1) {
|
||||
await genshin.returnMainUi();
|
||||
|
||||
keyPress("M"); await sleep(1000);
|
||||
for (let i = 5; i > 0; --i) {
|
||||
click(46, 436); await sleep(500); // zoom in
|
||||
}
|
||||
for (let i = 0; i < level; ++i) {
|
||||
click(46, 630); await sleep(500); // zoom out
|
||||
}
|
||||
// keyPress("M"); await sleep(1000);
|
||||
}
|
||||
|
||||
// 拖拽地图
|
||||
async function dragMap(byX, byY) {
|
||||
await genshin.returnMainUi();
|
||||
|
||||
let byL = Math.sqrt(byX * byX + byY * byY);
|
||||
let d = 5;
|
||||
|
||||
let dx = Math.round(d * byX / byL);
|
||||
let dy = Math.round(d * byY / byL);
|
||||
|
||||
let times = Math.round(byX / dx * genshin.screenDpiScale);
|
||||
|
||||
log.debug(`byL: ${byL}; dx: ${dx}; dy: ${dy}; times: ${times}; genshin.screenDpiScale: ${genshin.screenDpiScale};`);
|
||||
|
||||
keyPress("M"); await sleep(1000);
|
||||
moveMouseBy(-byX, -byY); await sleep(300);
|
||||
|
||||
leftButtonDown(); await sleep(300);
|
||||
for (let i = 0; i < times; ++i) {
|
||||
moveMouseBy(dx, dy); await sleep(30);
|
||||
}
|
||||
leftButtonUp(); await sleep(300);
|
||||
}
|
||||
|
||||
// 就近传送(传送脚本文件中的第一个点)
|
||||
async function tpNearby(filePath) {
|
||||
const raw = file.ReadTextSync(filePath);
|
||||
const data = JSON.parse(raw);
|
||||
await genshin.tp(data['positions'][0]['x'], data['positions'][0]['y']);
|
||||
}
|
||||
|
||||
// 分解圣遗物
|
||||
async function salvage() {
|
||||
if (!autoSalvage) return;
|
||||
// 调整地图
|
||||
async function resizeMap(level = 1) {
|
||||
await genshin.returnMainUi();
|
||||
keyPress("B");
|
||||
await sleep(2000);
|
||||
click(670, 40);
|
||||
await sleep(1000); // 圣遗物
|
||||
click(660, 1010);
|
||||
await sleep(1000); // 分解
|
||||
click(300, 1020);
|
||||
await sleep(1000); // 快速选择
|
||||
|
||||
// 点击4星圣遗物
|
||||
if (!autoSalvage4) {
|
||||
click(200, 380);
|
||||
await sleep(500);
|
||||
keyPress("M"); await sleep(1000);
|
||||
for (let i = 5; i > 0; --i) {
|
||||
click(46, 436); await sleep(500);
|
||||
}
|
||||
for (let i = 0; i < level; ++i) {
|
||||
click(46, 630); await sleep(500);
|
||||
}
|
||||
|
||||
click(340, 1000);
|
||||
await sleep(1000); // 确认选择
|
||||
click(1720, 1015);
|
||||
await sleep(1500); // 分解
|
||||
click(1320, 750);
|
||||
await sleep(1000); // 进行分解
|
||||
|
||||
await genshin.returnMainUi();
|
||||
}
|
||||
|
||||
// 进度文件路径
|
||||
const progressFile = "progress.json";
|
||||
// 拖拽地图
|
||||
async function dragMap(byX, byY) {
|
||||
await genshin.returnMainUi();
|
||||
let byL = Math.sqrt(byX * byX + byY * byY);
|
||||
let d = 5;
|
||||
let dx = Math.round(d * byX / byL);
|
||||
let dy = Math.round(d * byY / byL);
|
||||
let times = Math.round(byX / dx * genshin.screenDpiScale);
|
||||
log.debug(`byL: ${byL}; dx: ${dx}; dy: ${dy}; times: ${times};`);
|
||||
keyPress("M"); await sleep(1000);
|
||||
moveMouseBy(-byX, -byY); await sleep(300);
|
||||
leftButtonDown(); await sleep(300);
|
||||
for (let i = 0; i < times; ++i) {
|
||||
moveMouseBy(dx, dy); await sleep(30);
|
||||
}
|
||||
leftButtonUp(); await sleep(300);
|
||||
}
|
||||
|
||||
// 初始化进度
|
||||
let progress = {
|
||||
path: null,
|
||||
completedTasks: [],
|
||||
lastRunDate: null // 记录上次运行的日期
|
||||
// 就近传送
|
||||
async function tpNearby(filePath) {
|
||||
const raw = file.ReadTextSync(filePath);
|
||||
const data = JSON.parse(raw);
|
||||
await genshin.tp(data['positions'][0]['x'], data['positions'][0]['y']);
|
||||
}
|
||||
|
||||
// OCR圣遗物分解配置
|
||||
const AUTO_SALVAGE_CONFIG = {
|
||||
autoSalvage3: settings.autoSalvage3 || "否",
|
||||
autoSalvage4: settings.autoSalvage4 || "否"
|
||||
};
|
||||
const OCR_REGIONS = {
|
||||
expStorage: { x: 1472, y: 883, width: 170, height: 34 },
|
||||
expCount: { x: 1472, y: 895, width: 170, height: 34 }
|
||||
};
|
||||
|
||||
// 获取本地日期(YYYY-MM-DD 格式)
|
||||
const numberReplaceMap = {
|
||||
"O": "0", "o": "0", "Q": "0", "0": "0",
|
||||
"I": "1", "l": "1", "i": "1", "1": "1", "一": "1",
|
||||
"Z": "2", "z": "2", "2": "2", "二": "2",
|
||||
"E": "3", "e": "3", "3": "3", "三": "3",
|
||||
"A": "4", "a": "4", "4": "4",
|
||||
"S": "5", "s": "5", "5": "5",
|
||||
"G": "6", "b": "6", "6": "6",
|
||||
"T": "7", "t": "7", "7": "7",
|
||||
"B": "8", "θ": "8", "8": "8",
|
||||
"g": "9", "q": "9", "9": "9",
|
||||
};
|
||||
|
||||
function processExpText(text) {
|
||||
let correctedText = text || "";
|
||||
let removedSymbols = [];
|
||||
for (const [wrong, correct] of Object.entries(numberReplaceMap)) {
|
||||
correctedText = correctedText.replace(new RegExp(wrong, 'g'), correct);
|
||||
}
|
||||
let finalText = '';
|
||||
for (const char of correctedText) {
|
||||
if (/[0-9]/.test(char)) finalText += char;
|
||||
else if (char.trim() !== '') removedSymbols.push(char);
|
||||
}
|
||||
return { processedText: finalText, removedSymbols: [...new Set(removedSymbols)] };
|
||||
}
|
||||
|
||||
async function recognizeExpRegion(regionName, ra = null, timeout = 2000) {
|
||||
const ocrRegion = OCR_REGIONS[regionName];
|
||||
if (!ocrRegion) {
|
||||
log.error(`[狗粮OCR] 无效区域:${regionName}`);
|
||||
return { success: false, expCount: 0 };
|
||||
}
|
||||
log.info(`[狗粮OCR] 识别${regionName}(x=${ocrRegion.x}, y=${ocrRegion.y})`);
|
||||
const startTime = Date.now();
|
||||
while (Date.now() - startTime < timeout) {
|
||||
try {
|
||||
const ocrResult = ra.find(RecognitionObject.ocr(
|
||||
ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height
|
||||
));
|
||||
log.info(`[狗粮OCR] 原始文本:${ocrResult.text}`);
|
||||
if (ocrResult?.text) {
|
||||
const { processedText } = processExpText(ocrResult.text);
|
||||
const expCount = processedText ? parseInt(processedText, 10) : 0;
|
||||
log.info(`[狗粮OCR] ${regionName}结果:${expCount}`);
|
||||
return { success: true, expCount };
|
||||
}
|
||||
} catch (error) {
|
||||
log.warn(`[狗粮OCR] 识别失败:${error.message}`);
|
||||
}
|
||||
await sleep(500);
|
||||
}
|
||||
log.error(`[狗粮OCR] ${regionName}超时未识别`);
|
||||
return { success: false, expCount: 0 };
|
||||
}
|
||||
|
||||
// 分解函数
|
||||
async function executeSalvageWithOCR(pathGroup, isPreRun = false) {
|
||||
if (!autoSalvage) {
|
||||
log.info("[狗粮分解] 未开启自动分解,跳过");
|
||||
return { success: false, totalExp: 0 };
|
||||
}
|
||||
|
||||
const runType = isPreRun ? "首次(路径前)" : "常规";
|
||||
const actualPathGroup = isPreRun ? [] : pathGroup;
|
||||
log.info(`[狗粮分解] 开始${runType}分解(关联路径组:共${actualPathGroup.length}个,均为已执行路径)`);
|
||||
|
||||
let storageExp = 0;
|
||||
let countExp = 0;
|
||||
let cachedFrame = null;
|
||||
|
||||
try {
|
||||
await genshin.returnMainUi();
|
||||
keyPress("B"); await sleep(1000);
|
||||
const coords = [
|
||||
[670, 40], // 打开背包
|
||||
[660, 1010], // 打开分解
|
||||
[300, 1020], // 分解选项页面
|
||||
[200, 300, 500, AUTO_SALVAGE_CONFIG.autoSalvage3 !== '是'], // 3星配置
|
||||
[200, 380, 500, AUTO_SALVAGE_CONFIG.autoSalvage4 !== '是'], // 4星配置
|
||||
[340, 1000], // 确认选择
|
||||
[1720, 1015], // 分解按钮
|
||||
[1320, 756], // 确认分解
|
||||
[1840, 45, 1500], // 退出
|
||||
[1840, 45], // 退出
|
||||
[1840, 45], // 退出
|
||||
];
|
||||
|
||||
for (const coord of coords) {
|
||||
const [x, y, delay = 1000, condition = true] = coord;
|
||||
if (condition) {
|
||||
click(x, y);
|
||||
await sleep(delay);
|
||||
if (x === 660 && y === 1010) {
|
||||
cachedFrame?.dispose();
|
||||
cachedFrame = captureGameRegion();
|
||||
const { expCount } = await recognizeExpRegion("expStorage", cachedFrame, 1000);
|
||||
storageExp = expCount;
|
||||
}
|
||||
if (x === 340 && y === 1000) {
|
||||
cachedFrame?.dispose();
|
||||
cachedFrame = captureGameRegion();
|
||||
const { expCount } = await recognizeExpRegion("expCount", cachedFrame, 1000);
|
||||
countExp = expCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const totalExp = countExp - storageExp;
|
||||
const actualExp = Math.max(totalExp, 0);
|
||||
log.info(`[狗粮分解] ${runType}分解完成,获得EXP:${actualExp}`);
|
||||
|
||||
// 记录逻辑
|
||||
const recordTime = new Date().toLocaleString();
|
||||
const pathGroupStr = actualPathGroup.map((p, i) => `${i+1}. ${p}`).join('\n ');
|
||||
const record = { time: recordTime, pathGroup: actualPathGroup, exp: actualExp, type: runType };
|
||||
salvageRecords.push(record);
|
||||
|
||||
if (!isPreRun) {
|
||||
totalSalvageExp += actualExp;
|
||||
}
|
||||
|
||||
// 日志与通知
|
||||
const logContent = `[${recordTime}] ${runType}分解 - 关联路径组(共${actualPathGroup.length}个):\n ${pathGroupStr}\n本次分解EXP:${actualExp}${!isPreRun ? `\n累计分解EXP:${totalSalvageExp}` : ''}`;
|
||||
await writeFile(salvageLogFile, logContent, true);
|
||||
|
||||
if (notify) {
|
||||
notification.send(`${runType}分解完成!\n关联已执行路径:${actualPathGroup.length}个\n本次EXP:${actualExp}${!isPreRun ? `\n累计EXP:${totalSalvageExp}` : ''}`);
|
||||
}
|
||||
|
||||
return { success: true, totalExp: actualExp };
|
||||
} catch (error) {
|
||||
log.error(`[狗粮分解] ${runType}分解失败:${error.message}`);
|
||||
return { success: false, totalExp: 0 };
|
||||
} finally {
|
||||
cachedFrame?.dispose();
|
||||
await genshin.returnMainUi();
|
||||
}
|
||||
}
|
||||
|
||||
// 进度管理
|
||||
const progressFile = "progress.json";
|
||||
let progress = { path: null, completedTasks: [], lastRunDate: null };
|
||||
|
||||
function getLocalDate() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需要加 1
|
||||
const date = String(now.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${date}`;
|
||||
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
||||
}
|
||||
// 读取进度
|
||||
|
||||
async function loadProgress() {
|
||||
try {
|
||||
const content = await file.readText(progressFile);
|
||||
const loadedProgress = JSON.parse(content);
|
||||
|
||||
// 获取本地日期
|
||||
const today = getLocalDate();
|
||||
|
||||
// 获取保存的日期
|
||||
const lastRunDate = loadedProgress.lastRunDate;
|
||||
|
||||
if (lastRunDate && lastRunDate === today) {
|
||||
// 如果日期一致,加载进度
|
||||
if (loadedProgress.lastRunDate === today) {
|
||||
progress.path = loadedProgress.path;
|
||||
progress.completedTasks = Array.isArray(loadedProgress.completedTasks) ? loadedProgress.completedTasks : [];
|
||||
} else {
|
||||
// 如果日期不一致,重置进度
|
||||
progress.path = null;
|
||||
progress.completedTasks = [];
|
||||
}
|
||||
|
||||
// 更新当前日期
|
||||
progress.lastRunDate = today;
|
||||
// 日志输出
|
||||
log.info(`加载进度成功: ${JSON.stringify(progress)}`);
|
||||
} catch (error) {
|
||||
log.error("加载进度失败:", error);
|
||||
}
|
||||
}
|
||||
// 保存进度
|
||||
|
||||
async function saveProgress() {
|
||||
try {
|
||||
// 获取本地日期
|
||||
const today = getLocalDate();
|
||||
|
||||
// 更新进度并保存
|
||||
progress.lastRunDate = today;
|
||||
progress.lastRunDate = getLocalDate();
|
||||
await file.writeText(progressFile, JSON.stringify(progress));
|
||||
|
||||
log.info(`进度已保存,当前日期: ${today}`);
|
||||
log.info(`进度已保存,当前日期: ${progress.lastRunDate}`);
|
||||
} catch (error) {
|
||||
log.error("保存进度失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let count = 0; // 用于记录分解圣遗物的次数
|
||||
|
||||
// 核心执行函数(关键修正:先执行路径,再加入分解组并判断)
|
||||
async function runFile(filePath, times = 2) {
|
||||
try {
|
||||
// 检查任务是否已经完成
|
||||
if (progress.completedTasks.includes(filePath)) {
|
||||
log.info(`任务已跳过: ${filePath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录任务开始时间
|
||||
const startTime = Date.now();
|
||||
log.info(`开始执行任务: ${filePath}`);
|
||||
|
||||
// 检查是否是恢复任务
|
||||
let isToRestore = filePath.search("(恢复)") != -1;
|
||||
if (isToRestore && !activeRestore) {
|
||||
log.info(`跳过恢复任务: ${filePath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 分解圣遗物
|
||||
if (!isToRestore && count++ % autoSalvageSpan == 0) {
|
||||
await salvage();
|
||||
// 步骤1:先执行路径(确保路径已完成)
|
||||
let forceInteraction = filePath.search("-f") != -1;
|
||||
if (!isToRestore) dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false }));
|
||||
|
||||
await pathingScript.runFile(filePath); // 执行路径
|
||||
await sleep(1000);
|
||||
dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false }));
|
||||
|
||||
// 步骤2:路径执行完成后,再加入待分解组
|
||||
if (!isToRestore) {
|
||||
pendingPaths.push(filePath);
|
||||
log.debug(`路径已执行完成,加入待分解组:${filePath},当前组含${pendingPaths.length}个已执行路径`);
|
||||
}
|
||||
|
||||
// 调整地图缩放 (bgi[v0.41.0]后不需要)
|
||||
// let shouldResizeMap = filePath.search("-m") != -1;
|
||||
// if (shouldResizeMap) await resizeMap();
|
||||
// 步骤3:判断是否触发分解(仅针对已执行的路径)
|
||||
if (!isToRestore && ++count % autoSalvageSpan == 0) {
|
||||
log.debug(`已执行${count}个路径,达到分解间隔(${autoSalvageSpan}个),触发常规分解`);
|
||||
await executeSalvageWithOCR([...pendingPaths], false);
|
||||
pendingPaths = []; // 清空组,准备收集下一批已执行路径
|
||||
}
|
||||
|
||||
// 配置自动拾取,根据文件名指定信息,确定是否强制交互(快速拾取)
|
||||
let forceInteraction = filePath.search("-f") != -1;
|
||||
if (!isToRestore) dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": forceInteraction }));
|
||||
|
||||
// 执行任务
|
||||
await pathingScript.runFile(filePath);
|
||||
await sleep(1000);
|
||||
// 配置强制拾取为关闭状态
|
||||
dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false }));
|
||||
// 记录任务结束时间
|
||||
// 保存进度
|
||||
const endTime = Date.now();
|
||||
const duration = endTime - startTime; // 任务运行时长(毫秒)
|
||||
|
||||
// 判断任务是否成功完成
|
||||
if (duration < 5000) { // 假设任务运行时长小于5秒则认为任务被取消或异常终止
|
||||
log.info(`任务运行时长过短(${duration}ms),可能被取消或异常终止,不保存进度: ${filePath}`);
|
||||
if (endTime - startTime < 5000) {
|
||||
log.info(`任务时长过短,不保存进度: ${filePath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 任务成功完成,更新进度
|
||||
progress.completedTasks.push(filePath);
|
||||
await saveProgress();
|
||||
log.info(`任务成功完成并保存进度: ${filePath}`);
|
||||
log.info(`任务完成并保存进度: ${filePath}`);
|
||||
|
||||
// 地图缩放按键同某些地图标识重叠,导致识别失败(bgi[v0.43.0]后引入)
|
||||
// // 完成路径后,放大地图,脚本中调用就近传送。仍可能被缩小回去。不可行
|
||||
// let shouldResizeMap_after = filePath.search("~m") != -1;
|
||||
// if (shouldResizeMap_after) {
|
||||
// await resizeMap(0);
|
||||
// await tpNearby(filePath);
|
||||
// }
|
||||
// 完成路径后,拖拽地图,脚本中调用就近传送。可行
|
||||
let shouldDragMap_after = filePath.search("~m") != -1;
|
||||
if (shouldDragMap_after) {
|
||||
if (filePath.search("~m") != -1) {
|
||||
await dragMap(-50, 50);
|
||||
await tpNearby(filePath);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 任务失败,记录错误但不保存进度
|
||||
log.error(`任务执行失败: ${filePath}`, error);
|
||||
await sleep(3000);
|
||||
if (times > 0) {
|
||||
log.info(`任务失败,尝试重新执行: ${filePath}`);
|
||||
log.info(`重试任务: ${filePath}`);
|
||||
await runFile(filePath, times - 1);
|
||||
} else {
|
||||
log.info(`任务失败,不再重试: ${filePath}`);
|
||||
log.info(`任务不再重试: ${filePath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,55 +416,95 @@ async function runFile(filePath, times = 2) {
|
||||
// 批量执行
|
||||
async function batch(folder, files) {
|
||||
for (let file of files) {
|
||||
const filePath = folder + file;
|
||||
await runFile(filePath);
|
||||
await runFile(folder + file);
|
||||
}
|
||||
}
|
||||
|
||||
// 文件写入函数
|
||||
async function writeFile(filePath, content, isAppend = false, maxRecords = 36500) {
|
||||
try {
|
||||
if (isAppend) {
|
||||
let existingContent = "";
|
||||
try { existingContent = await file.readTextSync(filePath); } catch (err) {}
|
||||
const records = existingContent.split("\n\n").filter(Boolean);
|
||||
const allRecords = [content, ...records];
|
||||
const finalContent = allRecords.slice(0, maxRecords).join("\n\n");
|
||||
return file.WriteTextSync(filePath, finalContent, false);
|
||||
} else {
|
||||
return file.WriteTextSync(filePath, content, false);
|
||||
}
|
||||
} catch (error) {
|
||||
const result = file.WriteTextSync(filePath, content, false);
|
||||
log.info(result ? `[日志] 处理成功: ${filePath}` : `[日志] 处理失败: ${filePath}`);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
(async function () {
|
||||
totalSalvageExp = 0;
|
||||
salvageRecords.length = 0;
|
||||
pendingPaths = [];
|
||||
count = 0;
|
||||
|
||||
// 如果 activeProgress 为否,则直接写入一个空对象到进度文件
|
||||
if (!activeProgress) {
|
||||
// 写入空对象到进度文件
|
||||
const result = await file.writeText(progressFile, JSON.stringify({}));
|
||||
if (result) {
|
||||
log.info("进度文件已重置,重新开始任务。");
|
||||
} else {
|
||||
log.error("进度文件重置失败。");
|
||||
}
|
||||
log.info(result ? "进度文件已重置" : "进度文件重置失败");
|
||||
} else {
|
||||
// 如果 activeProgress 为是,则正常加载进度
|
||||
await loadProgress();
|
||||
}
|
||||
|
||||
// 确定路径
|
||||
determinePath();
|
||||
|
||||
// 初始化
|
||||
await init();
|
||||
|
||||
// 执行主线任务
|
||||
log.info(`开始执行${progress.path}线路。`);
|
||||
// 路径前首次分解(无关联路径)
|
||||
if (autoSalvage) {
|
||||
log.info("路径前执行首次分解(不计入累计)");
|
||||
await executeSalvageWithOCR([], true);
|
||||
}
|
||||
|
||||
// 执行主路径(按顺序执行,每个路径完成后加入分解组)
|
||||
log.info(`开始执行${progress.path}线路`);
|
||||
if (progress.path == 'A') {
|
||||
await batch(folderA, pathingA);
|
||||
} else {
|
||||
await batch(folderB, pathingB);
|
||||
}
|
||||
|
||||
// 执行额外任务
|
||||
// 执行额外路径
|
||||
if (extra) {
|
||||
await init();
|
||||
log.info("开始执行额外线路。");
|
||||
log.info("开始执行额外线路");
|
||||
await batch(folderE, pathingE);
|
||||
if (path == 'A' || !extraAB) await batch(folderE, pathingE_A);
|
||||
if (path == 'B' || !extraAB) await batch(folderE, pathingE_B);
|
||||
}
|
||||
|
||||
// 24小时刷新的额外点位隔天拾取,避免空跑
|
||||
if (path == 'A' || extraAB == false) await batch(folderE, pathingE_A);
|
||||
if (path == 'B' || extraAB == false) await batch(folderE, pathingE_B);
|
||||
// 最终分解剩余已执行路径
|
||||
if (pendingPaths.length > 0 && autoSalvage) {
|
||||
log.info(`任务结束,分解剩余${pendingPaths.length}个已执行路径`);
|
||||
await executeSalvageWithOCR([...pendingPaths], false);
|
||||
pendingPaths = [];
|
||||
}
|
||||
|
||||
await init();
|
||||
log.info(`今日狗粮拾取任务完成。拾取路线:${progress.path}${extra ? '+E' : ''}`);
|
||||
log.info(`今日狗粮任务完成,路线:${progress.path}${extra ? '+E' : ''}`);
|
||||
await sleep(1000);
|
||||
})();
|
||||
// 最终汇总
|
||||
const summaryTime = new Date().toLocaleString();
|
||||
const summaryContent = `[${summaryTime}] 今日分解总览:
|
||||
总次数:${salvageRecords.length}次
|
||||
有效累计EXP(不含首次分解):${totalSalvageExp}
|
||||
分解明细:
|
||||
${salvageRecords.map((r, i) => `${i+1}. [${r.time}] ${r.type}分解:${r.exp} EXP`).join('\n')}`;
|
||||
|
||||
await writeFile(salvageLogFile, `\n===== 今日汇总 =====\n${summaryContent}\n====================\n`, true);
|
||||
log.info("\n===== 今日分解汇总 =====");
|
||||
log.info(summaryContent);
|
||||
log.info("=======================");
|
||||
|
||||
if (notify) {
|
||||
notification.send(`今日任务完成!\n总分解次数:${salvageRecords.length}次\n累计EXP(不含首次):${totalSalvageExp}\n路线:${progress.path}${extra ? '+额外' : ''}`);
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "狗粮ABE路线,自动拾取分解",
|
||||
"version": "2.5.4",
|
||||
"version": "2.5.5",
|
||||
"bgi_version": "0.45.1",
|
||||
"description": "圣遗物狗粮AB及额外路线,自动轮换,自动分解,就近恢复。",
|
||||
"saved_files": [
|
||||
"狗粮分解日志.txt",
|
||||
"progress.json"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Yang-z",
|
||||
@@ -12,4 +16,4 @@
|
||||
],
|
||||
"settings_ui": "settings.json",
|
||||
"main": "main.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,25 +9,63 @@
|
||||
"自动"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "swapPath",
|
||||
"type": "select",
|
||||
"label": "自动选择路线时,交换AB路线(默认:否)",
|
||||
"options": [
|
||||
"是",
|
||||
"否"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "activeProgress",
|
||||
"type": "select",
|
||||
"label": "启用断点续跑。多账号选否!(默认:否)",
|
||||
"label": "----------------------------------\n启用断点续跑。(默认:否)\n↓↓↓↓↓ 【多账号】请选:否 ↓↓↓↓↓",
|
||||
"options": [
|
||||
"是",
|
||||
"否"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "autoSalvage",
|
||||
"type": "select",
|
||||
"label": "----------------------------------\n自动分解(默认:是)",
|
||||
"options": [
|
||||
"是",
|
||||
"否"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "notify",
|
||||
"type": "checkbox",
|
||||
"label": "储存有本地记录\n----------------------------------\n分解经验后是否发送通知(默认:否)"
|
||||
},
|
||||
{
|
||||
"name": "autoSalvage3",
|
||||
"type": "select",
|
||||
"label": "需在BGI开启JS通知,并设置通知地址\n----------------------------------\n自动分解包括3星圣遗物(默认:否)",
|
||||
"options": [
|
||||
"是",
|
||||
"否"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "autoSalvage4",
|
||||
"type": "select",
|
||||
"label": "----------------------------------\n自动分解包括4星圣遗物(默认:否)",
|
||||
"options": [
|
||||
"是",
|
||||
"否"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "autoSalvageSpan",
|
||||
"type": "input-text",
|
||||
"label": "----------------------------------\n自动分解在多少路径后执行一次(默认:10)\n填入1则每路径都分解"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "swapPath",
|
||||
"type": "select",
|
||||
"label": "----------------------------------\n自动选择路线时,交换AB路线(默认:否)",
|
||||
"options": [
|
||||
"是",
|
||||
"否"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "extra",
|
||||
"type": "select",
|
||||
@@ -40,37 +78,12 @@
|
||||
{
|
||||
"name": "extraAB",
|
||||
"type": "select",
|
||||
"label": "满24小时才刷新的额外点位也分AB路线拾取(默认:是)",
|
||||
"label": "满24小时才刷新的额外点位\n也分AB路线拾取(默认:是)",
|
||||
"options": [
|
||||
"是",
|
||||
"否"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "autoSalvage",
|
||||
"type": "select",
|
||||
"label": "自动分解(默认:是)",
|
||||
"options": [
|
||||
"是",
|
||||
"否"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "autoSalvage4",
|
||||
"type": "select",
|
||||
"label": "自动分解包括4星圣遗物(默认:否)",
|
||||
"options": [
|
||||
"是",
|
||||
"否"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "autoSalvageSpan",
|
||||
"type": "input-text",
|
||||
"label": "自动分解在多少路径后执行一次(默认:10)"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "activeRestore",
|
||||
"type": "select",
|
||||
|
||||
Reference in New Issue
Block a user