js 纪行15次秘境 优化 (#2430)

* Delete repo/js/纪行周常-15次秘境一条龙/main.js

* Add files via upload

* Update main.js
This commit is contained in:
Tool_tingsu
2025-12-05 13:05:42 +08:00
committed by GitHub
parent 2a47d148f3
commit 7dccd68ea4

View File

@@ -1,214 +1,234 @@
(async function () { (async function () {
// 1. 设置游戏分辨率 // 1. 基础设置
setGameMetrics(1920, 1080, 1); setGameMetrics(1920, 1080, 1);
// 2. 返回主界面 // 定义全局变量以便在 finally 中释放
await genshin.returnMainUi(); let mats = {};
log.info("已返回主界面"); let fightOcrRo = null; // 用于战斗判断的OCR对象
// 3. 切换队伍 try {
if (settings.PartyName) { // --- 资源加载阶段 ---
log.info("正在加载图片资源...");
mats = {
enter: file.readImageMatSync("assets/images/华池岩岫.png"),
solo: file.readImageMatSync("assets/images/单人挑战.png"),
start: file.readImageMatSync("assets/images/开始挑战.png"),
leyline: file.readImageMatSync("assets/images/地脉异常.png"),
activate: file.readImageMatSync("assets/images/启动.png")
};
// 模版匹配对象
const ro = {
enter: RecognitionObject.TemplateMatch(mats.enter),
solo: RecognitionObject.TemplateMatch(mats.solo),
start: RecognitionObject.TemplateMatch(mats.start),
leyline: RecognitionObject.TemplateMatch(mats.leyline),
activate: RecognitionObject.TemplateMatch(mats.activate)
};
// --- 照搬 123.js 的 OCR 对象逻辑 ---
// 识别全屏范围
fightOcrRo = RecognitionObject.Ocr(0, 0, genshin.width, genshin.height);
log.info("资源加载完成");
// 2. 准备工作
await genshin.returnMainUi();
if (settings.partyName) {
await genshin.switchParty(settings.partyName); await genshin.switchParty(settings.partyName);
} }
// 4. 刷取循环 // 3. 循环逻辑
const maxAttempts = settings.loopTimes || 15; const maxAttempts = settings.loopTimes || 15;
let successCount = 0; let successCount = 0;
let attemptCount = 0;
// 定义识别对象(使用相对路径) for (let i = 0; i < maxAttempts; i++) {
const enterDungeonRo = RecognitionObject.TemplateMatch( log.info(`=== 开始第 ${i + 1}/${maxAttempts} 次循环 ===`);
file.readImageMatSync("assets/images/华池岩岫.png")
);
const soloChallengeRo = RecognitionObject.TemplateMatch( // a. 传送
file.readImageMatSync("assets/images/单人挑战.png")
);
const startChallengeRo = RecognitionObject.TemplateMatch(
file.readImageMatSync("assets/images/开始挑战.png")
);
const leylineDisorderRo = RecognitionObject.TemplateMatch(
file.readImageMatSync("assets/images/地脉异常.png")
);
const activateRo = RecognitionObject.TemplateMatch(
file.readImageMatSync("assets/images/启动.png")
);
// OCR对象用于检测战斗文本
const ocrRo2 = RecognitionObject.Ocr(0, 0, genshin.width, genshin.height);
// 封装识别图片并点击的函数
async function findAndClick(ro, maxRetries = 2, retryInterval = 1000, timeout = null) {
let retryCount = 0;
const startTime = Date.now();
while (retryCount < maxRetries && (timeout === null || Date.now() - startTime < timeout)) {
const capture = captureGameRegion();
const result = capture.find(ro);
capture.dispose();
if (!result.isEmpty()) {
result.click();
await sleep(30);
result.click();
return true;
}
retryCount++;
await sleep(retryInterval);
}
return false;
}
while (attemptCount < maxAttempts) {
attemptCount++;
log.info(`开始第 ${attemptCount}/${maxAttempts} 次挑战`);
let stepSuccess = false;
// a. 传送至点位
await genshin.tp("1436.2861328125", "1289.95556640625"); await genshin.tp("1436.2861328125", "1289.95556640625");
log.info("已传送至目标点位"); await sleep(2500);
await sleep(2000);
// b. 前进并检测"华池岩岫" // b. 寻找秘境入口
log.info("寻找秘境入口...");
keyDown("w"); keyDown("w");
const enterDungeonTimeout = 3000; const foundEnter = await waitFindTemplate(ro.enter, 3000);
const enterDungeonStartTime = Date.now();
let foundEnterDungeon = false;
while (Date.now() - enterDungeonStartTime < enterDungeonTimeout) {
const capture = captureGameRegion();
const result = capture.find(enterDungeonRo);
capture.dispose();
if (!result.isEmpty()) {
foundEnterDungeon = true;
break;
}
await sleep(100);
}
keyUp("w"); keyUp("w");
if (!foundEnterDungeon) { if (!foundEnter) {
log.warn("未找到'进入秘境',重试中..."); log.warn("未找到秘境入口,重置位置");
await genshin.returnMainUi(); await genshin.returnMainUi();
continue; continue;
} }
log.info("找到'进入秘境'"); log.info("找到入口");
await sleep(500);
// c. 按F键并检测"单人挑战"
keyPress("f"); keyPress("f");
await sleep(5000); await sleep(1500);
const foundSoloChallenge = await findAndClick(soloChallengeRo); // c. 点击单人挑战
if (!foundSoloChallenge) { if (!await waitAndClickTemplate(ro.solo, 5000)) {
log.error("未找到'单人挑战'终止脚本"); log.error("未找到'单人挑战'重试");
break; continue;
} }
log.info("已找到'单人挑战'");
await sleep(2000);
// d. 检测"开始挑战"
const foundStartChallenge = await findAndClick(startChallengeRo, 2, 3000);
if (!foundStartChallenge) {
log.error("未找到'开始挑战',终止脚本");
break;
}
log.info("已找到'开始挑战'");
await sleep(3000);
// e. 检测"地脉异常"
const leylineDisorderTimeout = 120000;
const foundLeylineDisorder = await findAndClick(leylineDisorderRo, 999, 1000, leylineDisorderTimeout);
if (!foundLeylineDisorder) {
log.error("未找到'地脉异常',超时终止");
break;
}
log.info("已找到'地脉异常'");
await sleep(1000); await sleep(1000);
// f. 前进并检测"启动" // d. 点击开始挑战
if (!await waitAndClickTemplate(ro.start, 5000)) {
log.error("未找到'开始挑战'");
continue;
}
log.info("进入加载...");
await sleep(5000); // 强制等待读条开始
// e. 确认进入副本并点击 (检测地脉异常)
// 123.js 原逻辑是找到就点,这里保持一致,作为判断进本的依据
if (await waitAndClickTemplate(ro.leyline, 120000)) {
log.info("已确认进入副本");
await sleep(1500);
} else {
log.error("进入副本超时");
break;
}
// f. 走向钥匙并启动
log.info("走向启动钥匙...");
keyDown("w"); keyDown("w");
const activateTimeout = 10000; const foundActivate = await waitFindTemplate(ro.activate, 10000);
const foundActivate = await findAndClick(activateRo, 999, 100, activateTimeout);
keyUp("w"); keyUp("w");
if (!foundActivate) { if (foundActivate) {
log.error("找到'启动',超时终止"); log.info("找到启动,开始战斗");
break;
}
log.info("已找到'启动'");
keyPress("f"); keyPress("f");
await sleep(1000); await sleep(1000);
// g. 开始自动战斗 // g. === 自动战斗 (完全复刻 123.js 的逻辑) ===
log.info("开始自动战斗"); // 使用 SoloTask("AutoFight") + CancellationToken + 循环OCR检测
const fightResult = await autoFight(120000); // 120秒战斗超时 const fightResult = await autoFightLike123js(fightOcrRo, 120000);
if (fightResult) { if (fightResult) {
successCount++; successCount++;
log.info(`战斗成功!当前完成 ${successCount}`); log.info(`战斗成功!当前完成 ${successCount}`);
await sleep(2000);
} else { } else {
log.error("战斗失败,终止脚本"); log.error("战斗失败,终止脚本");
break; break;
} }
} else {
log.error("未找到启动钥匙");
}
} }
await genshin.tp("1436.2861328125", "1289.95556640625"); await genshin.tp("1436.2861328125", "1289.95556640625");
log.info(`脚本结束。成功完成 ${successCount}/${attemptCount} 次挑战`); log.info(`脚本结束。成功完成 ${successCount}/${maxAttempts} 次挑战`);
// 自动战斗函数 } catch (e) {
async function autoFight(timeout) { log.error("脚本运行出错: " + e.message);
} finally {
// --- 资源释放 (保持内存安全) ---
for (let key in mats) {
if (mats[key]) mats[key].Dispose();
}
if (fightOcrRo && fightOcrRo.Dispose) {
fightOcrRo.Dispose();
}
log.info("资源已释放");
}
// ================= 辅助函数 =================
/**
* 1. 启动 AutoFight 任务
* 2. 循环截图 OCR
* 3. 识别关键字 ["挑战成功", "达成", "挑战达成"]
* 4. 识别成功后取消任务
*/
async function autoFightLike123js(ocrRo, timeout) {
const cts = new CancellationTokenSource(); const cts = new CancellationTokenSource();
// 启动后台战斗任务
dispatcher.runTask(new SoloTask("AutoFight"), cts); dispatcher.runTask(new SoloTask("AutoFight"), cts);
const startTime = Date.now(); const startTime = Date.now();
let fightResult = false; let fightResult = false;
log.info("战斗开始");
while (Date.now() - startTime < timeout) { while (Date.now() - startTime < timeout) {
const ro = captureGameRegion(); // 获取截图 (注意这里增加了Dispose以防止内存溢出逻辑与123.js一致)
if (recognizeFightText(ro)) { let capture = captureGameRegion();
ro.dispose(); try {
// 使用传入的 ocrRo 进行查找
let result = capture.find(ocrRo);
let text = result.text;
// 123.js 的判断关键字
const keywords = ["挑战成功", "达成", "挑战达成"];
let found = false;
for (const keyword of keywords) {
if (text.includes(keyword)) {
found = true;
break;
}
}
if (found) {
fightResult = true; fightResult = true;
break; break;
} }
ro.dispose(); } catch (err) {
await sleep(1000); log.error(`OCR识别出错: ${err}`);
} finally {
// 123.js 缺少这一步,这里加上以保证长时间挂机不崩
capture.Dispose();
} }
await sleep(1000); // 123.js 的间隔是 1000ms
}
// 停止战斗
cts.cancel(); cts.cancel();
return fightResult; return fightResult;
} }
// 战斗文本识别函数 // 模版点击辅助函数 (内存安全版)
function recognizeFightText(captureRegion) { async function waitAndClickTemplate(ro, timeoutMs) {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
let capture = captureGameRegion();
try { try {
const result = captureRegion.find(ocrRo2); let res = capture.find(ro);
const text = result.text; if (!res.isEmpty()) {
const keywords = ["挑战成功", "达成", "挑战达成"]; res.click();
// 123.js 的逻辑是点两下,这里保留双击逻辑以防万一
for (const keyword of keywords) { await sleep(30);
if (text.includes(keyword)) { res.click();
return true; return true;
} }
} finally {
capture.Dispose();
}
await sleep(1000); // 123.js 默认间隔较长,这里稍微对齐
} }
return false; return false;
} catch (error) {
log.error("OCR识别出错: {0}", error);
return false;
}
} }
// 模版查找辅助函数 (内存安全版)
async function waitFindTemplate(ro, timeoutMs, intervalMs = 200) {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
let capture = captureGameRegion();
try {
let res = capture.find(ro);
if (!res.isEmpty()) return true;
} finally {
capture.Dispose();
}
await sleep(intervalMs);
}
return false;
}
})(); })();