Files
mno eabb636ab2 js:路线测试归档 (#2827)
该js目前仅用于锄地路线制作内部使用,不适合进入仓库(已误导400人次)
2026-01-31 17:06:06 +08:00

809 lines
35 KiB
JavaScript
Raw Permalink 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.
// 主逻辑
(async function () {
const name1 = "锄地路线测试";
const duration1 = 1234; // 1.234 秒
await fakeLog(name1, true, false, duration1);
if (settings.activatePickup) {
dispatcher.addTimer(new RealtimeTimer("AutoPick"));
}
const startRouteNumber = +settings.startRouteNumber || 1;
const pathingFolderPath = "pathing";
const resultFolderPath = "records";
const fileInfo = file.readTextSync("assets/info.json");
const infoData = JSON.parse(fileInfo);
if (!settings.doTest) {
const startTime = new Date();
const formattedStartTime = startTime.toISOString().replace(/[^0-9]/g, '');
const recordFileName = `${formattedStartTime}.json`;
const recordFilePath = resultFolderPath + '/' + recordFileName;
const routes = await readFolder(pathingFolderPath, true);
let i = startRouteNumber - 2;
let Mora = -1;
let attempts = 0;
while (attempts < 5) {
const result = await mora();
if (result !== null) {
Mora = parseInt(result.match(/\d+/g).join(''), 10);
break;
}
attempts++;
log.warn(`获取的 mora 值为 null尝试次数 ${attempts}/5重新获取...`);
}
if (Mora === -1) log.warn('尝试 5 次后仍未获取到有效的 mora 值,记为 -1');
let MonsterInfo = await getMonsterCounts();
let routeTime = new Date();
for (let k = 1; k < routes.length + 1; k++) {
i++;
if ((i + 1) === (+settings.endRouteNumber)) {
break;
}
await genshin.tpToStatueOfTheSeven();
if (settings.underwater) {
await pathingScript.runFile("assets/学习螃蟹技能.json");
}
const route = routes[i % routes.length];
log.info(`完整路径:${route.fullPath}`);
route.expectMora = 0;
route.eliteNum = 0;
route.normalNum = 0;
const duration2 = 0;
await fakeLog(route.fullPath, false, true, duration2);
routeTime = new Date();
log.info(`这是第 ${(i % routes.length) + 1}条路线:${route.fullPath}`);
settings.startRouteNumber = (i % routes.length) + 2
await pathingScript.runFile(route.fullPath);
const newDate = new Date();
const timeDiffInSeconds = (newDate - routeTime) / 1000;
route.routeTime = timeDiffInSeconds;
const duration3 = 5000;
await fakeLog(route.fullPath, false, false, duration3);
try { await sleep(10); } catch (error) { log.error(`运行中断: ${error}`); break; }
const currentMonsterInfo = await getMonsterCounts();
const monsterDifferences = {};
for (const monster in currentMonsterInfo) {
if (currentMonsterInfo[monster] !== MonsterInfo[monster] &&
currentMonsterInfo[monster] !== -1 &&
MonsterInfo[monster] !== -1) {
monsterDifferences[monster] = currentMonsterInfo[monster] - MonsterInfo[monster];
}
}
route.monsterNum = monsterDifferences;
MonsterInfo = currentMonsterInfo;
let currentMora = -1;
attempts = 0;
while (attempts < 5) {
const result = await mora();
if (result !== null) {
currentMora = parseInt(result.match(/\d+/g).join(''), 10);
break;
}
attempts++;
log.warn(`获取的 mora 值为 null尝试次数 ${attempts}/5重新获取...`);
}
if (Mora === -1) log.warn('尝试 5 次后仍未获取到有效的 mora 值,记为 -1');
const moraDiff = currentMora - Mora;
route.moraDiff = moraDiff;
Mora = currentMora;
for (const [monsterName, count] of Object.entries(route.monsterNum)) {
const monsterInfo = infoData.find(item => item.name === monsterName);
if (monsterInfo) {
if (monsterInfo.type === "普通") {
route.normalNum += count;
route.expectMora += count * monsterInfo.moraRate * 40.5;
} else if (monsterInfo.type === "精英") {
route.eliteNum += count;
route.expectMora += count * monsterInfo.moraRate * 200;
}
}
}
const recordContent = JSON.stringify(routes.slice(startRouteNumber - 1, i + 1), null, 2);
try {
await file.writeText(recordFilePath, recordContent);
log.info(`记录文件已写入 ${recordFilePath}`);
} catch (error) {
log.error(`写入记录文件失败: ${error.message}`);
}
await sleep(1000);
}
} else {
log.info("doTest 设置为 false读取 records 文件夹中的文件");
const routes = await readFolder(pathingFolderPath, true);
log.info(`找到 ${routes.length} 个路径文件`);
const records = await readFolder("records", true);
log.info(`找到 ${records.length} 个记录文件`);
const recordMap = {}; // 🔥 文件名匹配
for (const record of records) {
log.info(`处理文件:${record.fullPath}`);
try {
const fileContent = file.readTextSync(record.fullPath);
const jsonData = JSON.parse(fileContent);
const filtered = jsonData.filter(r => {
if (r.routeTime < 10) {
log.warn(`过滤异常记录: ${r.fullPath} | 用时=${r.routeTime}s<10s`);
return false;
}
if (r.monsterNum && typeof r.monsterNum === 'object') {
for (const [k, v] of Object.entries(r.monsterNum)) {
if (typeof v !== 'number' || v < 0 || v > 50) {
log.warn(`修正异常数量: ${r.fullPath} | ${k}: ${v} → 0`);
r.monsterNum[k] = 0;
}
}
const nums = Object.values(r.monsterNum);
if (nums.length && nums.every(c => c === 0)) {
log.warn(`过滤全零记录: ${r.fullPath}`);
return false;
}
}
return true;
});
if (Array.isArray(filtered)) {
for (const entry of filtered) {
const fileName = entry.fullPath.split('\\').pop(); // 🔥 文件名匹配
if (!fileName) {
log.warn(`fileName 为空,跳过该记录`);
continue;
}
if (!recordMap[fileName]) recordMap[fileName] = []; // 🔥 文件名匹配
recordMap[fileName].push({
fullPath: entry.fullPath,
fileName, // 🔥 文件名匹配
monsterNum: entry.monsterNum,
moraDiff: entry.moraDiff,
routeTime: entry.routeTime,
expectMora: entry.expectMora,
normalNum: entry.normalNum,
eliteNum: entry.eliteNum
});
if (recordMap[fileName].length > 7) recordMap[fileName].shift(); // 🔥 文件名匹配
}
} else {
log.warn(`文件 ${record.fileName} 的内容不是数组,跳过该文件`);
}
} catch (error) {
log.error(`读取或解析文件 ${record.fileName} 时出错:${error.message}`);
}
}
const finalRecords = [];
for (const fileName in recordMap) { // 🔥 文件名匹配
const records = recordMap[fileName]; // 🔥 文件名匹配
const fields = ["routeTime"];
const processedRecord = { fileName, records: {} }; // 🔥 文件名匹配
// ================= 1. routeTime只认 >10 秒 =================
{
const values = records
.map(r => r.routeTime)
.filter(v => typeof v === 'number' && v > 10); // 🔥 只要 >10 秒
if (values.length === 0) {
processedRecord.records.routeTime = 0;
} else {
values.sort((a, b) => a - b);
const mid = Math.floor(values.length / 2);
processedRecord.records.routeTime = parseFloat(
(values.length % 2 === 0
? (values[mid - 1] + values[mid]) / 2
: values[mid]).toFixed(2)
);
}
}
// ================= 2. monsterNum0-40 只,缺项当 0 =================
processedRecord.records.monsterNum = {};
// 所有出现过的怪物名
const allMonsters = [...new Set(records.flatMap(r => Object.keys(r.monsterNum || {})))];
allMonsters.forEach(monster => {
// 逐条采样:没写就按 0 算
const values = records
.map(r => {
const v = r.monsterNum?.[monster]; // 可能为 undefined
const num = typeof v === 'number' ? v : 0; // 没写→0
return num >= 0 && num <= 40 ? num : NaN; // 超界当 NaN 扔
})
.filter(v => !isNaN(v)) // 只保留 0-40 的采样
.sort((a, b) => a - b);
if (values.length === 0) return; // 全部超界才真的跳过
const mid = Math.floor(values.length / 2);
const median = parseFloat(
(values.length % 2 === 0
? (values[mid - 1] + values[mid]) / 2
: values[mid]).toFixed(2)
);
// 中位数>0 才写,避免全是 0 还占字段
if (median > 0) processedRecord.records.monsterNum[monster] = median;
});
processedRecord.records.normalNum = 0;
processedRecord.records.eliteNum = 0;
processedRecord.records.expectMora = 0;
for (const [monsterName, count] of Object.entries(processedRecord.records.monsterNum)) {
const monsterInfo = infoData.find(item => item.name === monsterName);
if (monsterInfo) {
if (monsterInfo.type === "普通") {
processedRecord.records.normalNum += count;
processedRecord.records.expectMora += count * monsterInfo.moraRate * 40.5;
} else if (monsterInfo.type === "精英") {
processedRecord.records.eliteNum += count;
processedRecord.records.expectMora += count * monsterInfo.moraRate * 200;
}
}
}
finalRecords.push(processedRecord);
}
let matchedCount = 0;
let unmatchedCount = 0;
for (const { fileName, records } of finalRecords) { // 🔥 文件名匹配
const route = routes.find(r => r.fileName === fileName); // 🔥 文件名匹配
if (!route) {
log.warn(`未找到文件名对应的路线: ${fileName}`); // 🔥 文件名匹配
unmatchedCount++;
continue;
}
const fileContent = file.readTextSync(route.fullPath);
const jsonData = JSON.parse(fileContent);
const { routeTime, expectMora, normalNum, eliteNum, monsterNum } = records;
const refCount = recordMap[fileName] ? recordMap[fileName].length : 0; // 🔥 文件名匹配
const monsterDescription = Object.entries(monsterNum)
.map(([m, c]) => `${c}${m}`)
.join('、');
let newDescription;
if (eliteNum === 0 && normalNum === 0) {
newDescription = ` 路线信息:该路线预计用时${routeTime}秒,该路线不含任何精英或小怪。`;
} else {
newDescription = ` 路线信息:该路线预计用时${routeTime}秒,包含以下怪物:${monsterDescription}`;
}
jsonData.info.description = `${newDescription}`;
const targetFolder = refCount > 3 ? 'pathingOut' : 'pathingToCheck';
const modifiedFullPath = route.fullPath.replace('pathing', targetFolder);
await file.writeTextSync(modifiedFullPath, JSON.stringify(jsonData, null, 2));
log.info(`文件 ${route.fullPath} 的 description 已更新,本次共参考 ${refCount} 份历史记录`);
if (refCount <= 3) log.warn('参考记录少于等于 3 份,可信度较低,输出到 pathingToCheck 目录');
matchedCount++;
}
log.info(`总路径文件数:${routes.length}`);
log.info(`成功匹配并修改的文件数:${matchedCount}`);
log.info(`未匹配的记录数:${unmatchedCount}`);
}
const duration4 = 0;
await fakeLog(name1, true, true, duration4);
})();
async function readFolder(folderPath, onlyJson) {
const folderStack = [folderPath];
const files = [];
while (folderStack.length > 0) {
const currentPath = folderStack.pop();
const filesInSubFolder = file.ReadPathSync(currentPath);
const subFolders = [];
for (const filePath of filesInSubFolder) {
if (file.IsFolder(filePath)) {
subFolders.push(filePath);
} else {
if (onlyJson && !filePath.endsWith('.json')) continue;
const fileName = filePath.split('\\').pop();
const folderPathArray = filePath.split('\\').slice(0, -1);
files.push({ fullPath: filePath, fileName, folderPathArray });
}
}
folderStack.push(...subFolders.reverse());
}
return files;
}
async function readFolder(folderPath, onlyJson) {
log.info(`开始读取文件夹:${folderPath}`);
// 新增一个堆栈,初始时包含 folderPath
const folderStack = [folderPath];
// 新增一个数组,用于存储文件信息对象
const files = [];
// 当堆栈不为空时,继续处理
while (folderStack.length > 0) {
// 从堆栈中弹出一个路径
const currentPath = folderStack.pop();
// 读取当前路径下的所有文件和子文件夹路径
const filesInSubFolder = file.ReadPathSync(currentPath);
// 临时数组,用于存储子文件夹路径
const subFolders = [];
for (const filePath of filesInSubFolder) {
if (file.IsFolder(filePath)) {
// 如果是文件夹,先存储到临时数组中
subFolders.push(filePath);
} else {
// 如果是文件,根据 onlyJson 判断是否存储
if (onlyJson) {
if (filePath.endsWith(".json")) {
const fileName = filePath.split('\\').pop(); // 提取文件名
const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组
files.push({
fullPath: filePath,
fileName: fileName,
folderPathArray: folderPathArray
});
//log.info(`找到 JSON 文件:${filePath}`);
}
} else {
const fileName = filePath.split('\\').pop(); // 提取文件名
const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组
files.push({
fullPath: filePath,
fileName: fileName,
folderPathArray: folderPathArray
});
//log.info(`找到文件:${filePath}`);
}
}
}
// 将临时数组中的子文件夹路径按原顺序压入堆栈
folderStack.push(...subFolders.reverse()); // 反转子文件夹路径
}
return files;
}
async function getMonsterCounts() {
let failcount = 0;
/* 0. 读取怪物列表 */
const raw = file.readTextSync('assets/info.json');
const monsterList = JSON.parse(raw).map(it => it.name);
const monsterCounts = {};
/* 外层重试:最多 3 轮 */
for (let round = 1; round <= 3; round++) {
log.info(`===== 第 ${round} 轮获取怪物数量 =====`);
/* 1. 外层循环:最多 3 次进入生物志 */
let attempt = 0;
while (attempt < 3) {
attempt++;
log.info(`${attempt} 次尝试进入生物志`);
await genshin.returnMainUi();
keyPress('VK_ESCAPE');
await sleep(1500);
const archiveTpl = RecognitionObject.TemplateMatch(
file.readImageMatSync('assets/RecognitionObject/图鉴.png'), 0, 0, 1920, 1080);
if (!(await findAndClick(archiveTpl))) continue;
const faunaTpl = RecognitionObject.TemplateMatch(
file.readImageMatSync('assets/RecognitionObject/生物志.png'), 0, 0, 1920, 1080);
if (!(await findAndClick(faunaTpl))) continue;
click(1355, 532);
await sleep(2000);
break;
}
if (attempt >= 3) {
log.error('连续 3 次无法进入生物志,脚本终止');
await genshin.returnMainUi();
return {};
}
/* ===== 工具函数 ===== */
async function findAndClick(target, maxAttempts = 100) {
for (let attempts = 0; attempts < maxAttempts; attempts++) {
const gameRegion = captureGameRegion();
try {
const result = gameRegion.find(target);
if (result.isExist()) {
result.click();
await sleep(50);
return true; // 成功立刻返回
}
} catch (err) {
} finally {
gameRegion.dispose();
}
if (attempts < maxAttempts - 1) { // 最后一次不再 sleep
await sleep(50);
}
}
//log.error("已达到重试次数上限,仍未找到目标");
return false;
}
async function scrollPage(totalDistance, stepDistance = 10, delayMs = 5) {
moveMouseTo(400, 750); // 移动到屏幕水平中心垂直750坐标
await sleep(50);
leftButtonDown();
// 计算滚动方向和总步数
const isDownward = totalDistance < 0; // 如果totalDistance为负数则向下滑动
const steps = Math.ceil(Math.abs(totalDistance) / stepDistance); // 使用绝对值计算步数
for (let j = 0; j < steps; j++) {
const remainingDistance = Math.abs(totalDistance) - j * stepDistance;
const moveDistance = remainingDistance < stepDistance ? remainingDistance : stepDistance;
// 根据滚动方向调整移动方向
const direction = isDownward ? 1 : -1; // 向下滑动为正方向,向上滑动为负方向
moveMouseBy(0, 1.2 * direction * moveDistance); // 根据方向调整滚动方向
await sleep(delayMs);
}
await sleep(300);
leftButtonUp();
await sleep(1000);
}
async function readKillCount(maxTry = 10) {
const ocrObj = RecognitionObject.Ocr(865, 980, 150, 50);
for (let t = 0; t < maxTry; t++) {
const region = captureGameRegion();
const results = region.findMulti(ocrObj);
region.dispose();
for (let i = 0; i < results.count; i++) {
const str = results[i].text.trim();
// 必须是纯数字
if (/^\d+$/.test(str)) {
return { success: true, count: parseInt(str, 10) };
}
}
if (t < maxTry - 1) await sleep(25); // 最后一次不重试
}
return { success: false, count: -1 };
}
async function readKillCountStable(prevCount, sameTolerance = 5) {
let lastCount = -1;
for (let r = 0; r < sameTolerance; r++) {
await sleep(50 * r);
//log.info(`执行第${r}次ocr`)
const ocrRet = await readKillCount();
if (!ocrRet.success) break; // 真的读不到数字就放弃
lastCount = ocrRet.count;
if (lastCount !== prevCount) return { success: true, count: lastCount }; // 变了→成功
}
// 3 次仍相同→返回最后一次相同值
return { success: true, count: lastCount };
}
async function findMonsterIcon(monsterId, iconRetry = 3) {
const tpl = RecognitionObject.TemplateMatch(
file.readImageMatSync(`assets/monster/${monsterId.trim()}.png`), 130, 80, 670, 970);
let pageTurnsUp = 0;
while (pageTurnsUp < 1) {
let pageTurns = 0;
while (pageTurns < 2) {
//log.info("执行一次模板识别");
if (await findAndClick(tpl, iconRetry)) return true;
await scrollPage(400);
pageTurns++;
}
for (let j = 0; j < 2; j++) {
await scrollPage(-370);
}
pageTurnsUp++;
}
return false;
}
/* ===== 主循环 ===== */
let prevCount = -1; // 上一轮 OCR 结果
let retryMask = 0; // 位掩码:第 i 位为 1 表示已回退过
let prevFinalCount = -1; // 上一只怪物的最终击杀数
let continuousFail = 0; // 连续 -1 计数器(新增)
for (let i = 0; i < monsterList.length; i++) {
const monsterId = monsterList[i];
let time0 = new Date();
/* 1. 找怪 + OCR */
if (!(await findMonsterIcon(monsterId, 3))) {
log.info(`怪物: ${monsterId.trim()}, 未找到图标`);
failcount++;
if (failcount >= 10) {
break;
}
monsterCounts[monsterId.trim()] = -1;
prevCount = -1; // 重置
continuousFail++; // 新增
if (continuousFail >= 7) { // 新增
log.warn('连续 7 个怪物获取失败,中断本轮');
break; // 新增:中断本轮
}
continue;
}
let time1 = new Date();
//log.info(`寻找图标用时${time1 - time0}`);
/* 2. OCR与上一只结果比较原地重试 3 次 */
const ocr = await readKillCountStable(prevFinalCount, 3);
const count = ocr.success ? ocr.count : -1;
let time2 = new Date();
//log.info(`ocr用时${time2 - time1}`);
/* 2. 结果相同且本行还没回退过 → 回退一次 */
if (count === prevCount && !(retryMask & (1 << i))) {
retryMask |= (1 << i); // 标记已回退
i--; // 回退同一 i 一次
continue;
}
/* 3. 正常记录 */
monsterCounts[monsterId.trim()] = count;
log.info(`怪物: ${monsterId.trim()}, 数量: ${count}`);
prevCount = count;
prevFinalCount = count; // 记录本次最终值,供下一只比对
/* 新增:连续失败计数更新 */
if (count === -1) {
continuousFail++;
if (continuousFail >= 7) {
log.warn('连续 7 个怪物获取失败,中断本轮');
break;
}
} else {
continuousFail = 0; // 成功就清零
}
}
/* 本轮结束判定如果中途没有因“7连失败”跳出则认为成功 */
if (continuousFail < 7) {
log.info('所有怪物数量获取完成');
return monsterCounts;
}
/* 否则 continuousFail >=7自动进入下一轮重试 */
}
/* 3 轮都失败 */
log.error('3 轮重试后仍连续 7 次失败,放弃获取');
return monsterCounts;
}
// 定义 mora 函数
async function mora() {
// 定义所有图标的图像识别对象,每个图片都有自己的识别区域
let CharacterMenuRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/CharacterMenu.png"), 60, 991, 38, 38);
// 定义一个函数用于识别图像
async function recognizeImage(recognitionObject, timeout = 5000) {
log.info(`开始图像识别,超时时间: ${timeout}ms`);
let startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
// 尝试识别图像
let imageResult = captureGameRegion().find(recognitionObject);
if (imageResult) {
log.info(`成功识别图像,坐标: x=${imageResult.x}, y=${imageResult.y}`);
return { success: true, x: imageResult.x, y: imageResult.y };
}
} catch (error) {
log.error(`识别图像时发生异常: ${error.message}`);
}
await sleep(500); // 短暂延迟,避免过快循环
}
log.warn(`经过多次尝试,仍然无法识别图像`);
return { success: false };
}
// 定义一个函数用于识别文字并点击
async function recognizeTextAndClick(targetText, ocrRegion, timeout = 5000) {
log.info(`开始文字识别,目标文本: ${targetText},区域: x=${ocrRegion.x}, y=${ocrRegion.y}, width=${ocrRegion.width}, height=${ocrRegion.height}`);
let startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
// 尝试 OCR 识别
let resList = captureGameRegion().findMulti(RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height)); // 指定识别区域
// 遍历识别结果,检查是否找到目标文本
for (let res of resList) {
// 后处理:根据替换映射表检查和替换错误识别的字符
let correctedText = res.text;
if (correctedText.includes(targetText)) {
// 如果找到目标文本,计算并点击文字的中心坐标
let centerX = res.x + res.width / 2;
let centerY = res.y + res.height / 2;
log.info(`识别到目标文本: ${correctedText},点击坐标: x=${centerX}, y=${centerY}`);
await click(centerX, centerY);
await sleep(500); // 确保点击后有足够的时间等待
return { success: true, x: centerX, y: centerY };
}
}
} catch (error) {
log.warn(`页面标志识别失败,正在进行重试... 错误信息: ${error.message}`);
}
await sleep(1000); // 短暂延迟,避免过快循环
}
log.warn(`经过多次尝试,仍然无法识别文字: ${targetText}`);
return { success: false };
}
// 定义一个独立的函数用于在指定区域进行 OCR 识别并输出识别内容
async function recognizeTextInRegion(ocrRegion, timeout = 5000) {
log.info(`开始 OCR 识别,区域: x=${ocrRegion.x}, y=${ocrRegion.y}, width=${ocrRegion.width}, height=${ocrRegion.height}`);
let startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
// 在指定区域进行 OCR 识别
let ocrResult = captureGameRegion().find(RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height));
if (ocrResult) {
log.info(`OCR 识别成功,原始文本: ${ocrResult.text}`);
// 后处理:根据替换映射表检查和替换错误识别的字符
let correctedText = ocrResult.text;
log.info(`修正后文本: ${correctedText}`);
return correctedText; // 返回识别到的内容
} else {
log.warn(`OCR 识别区域未找到内容`);
return null; // 如果 OCR 未识别到内容,返回 null
}
} catch (error) {
log.error(`OCR 摩拉数识别失败,错误信息: ${error.message}`);
}
await sleep(500); // 短暂延迟,避免过快循环
}
log.warn(`经过多次尝试,仍然无法在指定区域识别到文字`);
return null; // 如果未识别到文字,返回 null
}
log.info("开始执行 mora 函数");
// 设置游戏分辨率和 DPI 缩放比例
setGameMetrics(1920, 1080, 1);
log.info("游戏分辨率和 DPI 设置完成");
// 返回游戏主界面
await genshin.returnMainUi();
log.info("返回游戏主界面");
// 按下 C 键
keyPress("C");
log.info("按下 C 键");
await sleep(1500);
let recognized = false;
// 识别“角色菜单”图标或“天赋”文字
let startTime = Date.now();
while (Date.now() - startTime < 5000) {
// 尝试识别“角色菜单”图标
let characterMenuResult = await recognizeImage(CharacterMenuRo, 5000);
if (characterMenuResult.success) {
await click(177, 433);
log.info("点击角色菜单图标");
await sleep(500);
recognized = true;
break;
}
// 尝试识别“天赋”文字
let targetText = "天赋";
let ocrRegion = { x: 133, y: 395, width: 115, height: 70 }; // 设置对应的识别区域
let talentResult = await recognizeTextAndClick(targetText, ocrRegion);
if (talentResult.success) {
log.info(`点击天赋文字,坐标: x=${talentResult.x}, y=${talentResult.y}`);
recognized = true;
break;
}
await sleep(1000); // 短暂延迟,避免过快循环
}
// 如果识别到了“角色菜单”或“天赋”,则识别“摩拉数值”
if (recognized) {
let ocrRegionMora = { x: 1620, y: 25, width: 152, height: 46 }; // 设置对应的识别区域
let recognizedText = await recognizeTextInRegion(ocrRegionMora);
if (recognizedText) {
log.info(`成功识别到摩拉数值: ${recognizedText}`);
return recognizedText; // 返回识别到的摩拉数值
} else {
log.warn("未能识别到摩拉数值。");
}
} else {
log.warn("未能识别到角色菜单或天赋,跳过摩拉数值识别。");
}
await sleep(500);
await genshin.returnMainUi();
log.info("返回游戏主界面");
return null; // 如果未能识别到摩拉数值,返回 null
}
async function fakeLog(name, isJs, isStart, duration) {
await sleep(10);
const currentTime = Date.now();
// 参数检查
if (typeof name !== 'string') {
log.error("参数 'name' 必须是字符串类型!");
return;
}
if (typeof isJs !== 'boolean') {
log.error("参数 'isJs' 必须是布尔型!");
return;
}
if (typeof isStart !== 'boolean') {
log.error("参数 'isStart' 必须是布尔型!");
return;
}
if (typeof currentTime !== 'number' || !Number.isInteger(currentTime)) {
log.error("参数 'currentTime' 必须是整数!");
return;
}
if (typeof duration !== 'number' || !Number.isInteger(duration)) {
log.error("参数 'duration' 必须是整数!");
return;
}
// 将 currentTime 转换为 Date 对象并格式化为 HH:mm:ss.sss
const date = new Date(currentTime);
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
const formattedTime = `${hours}:${minutes}:${seconds}.${milliseconds}`;
// 将 duration 转换为分钟和秒,并保留三位小数
const durationInSeconds = duration / 1000; // 转换为秒
const durationMinutes = Math.floor(durationInSeconds / 60);
const durationSeconds = (durationInSeconds % 60).toFixed(3); // 保留三位小数
// 使用四个独立的 if 语句处理四种情况
if (isJs && isStart) {
// 处理 isJs = true 且 isStart = true 的情况
const logMessage = `正在伪造js开始的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 开始执行JS脚本: "${name}"`;
log.debug(logMessage);
}
if (isJs && !isStart) {
// 处理 isJs = true 且 isStart = false 的情况
const logMessage = `正在伪造js结束的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 脚本执行结束: "${name}", 耗时: ${durationMinutes}${durationSeconds}\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------`;
log.debug(logMessage);
}
if (!isJs && isStart) {
// 处理 isJs = false 且 isStart = true 的情况
const logMessage = `正在伪造地图追踪开始的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 开始执行地图追踪任务: "${name}"`;
log.debug(logMessage);
}
if (!isJs && !isStart) {
// 处理 isJs = false 且 isStart = false 的情况
const logMessage = `正在伪造地图追踪结束的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 脚本执行结束: "${name}", 耗时: ${durationMinutes}${durationSeconds}\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------`;
log.debug(logMessage);
}
}