mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-05-24 23:05:49 +08:00
js 纪行15次秘境 优化 (#2430)
* Delete repo/js/纪行周常-15次秘境一条龙/main.js * Add files via upload * Update main.js
This commit is contained in:
@@ -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) {
|
// --- 资源加载阶段 ---
|
||||||
await genshin.switchParty(settings.partyName);
|
log.info("正在加载图片资源...");
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 刷取循环
|
mats = {
|
||||||
const maxAttempts = settings.loopTimes || 15;
|
enter: file.readImageMatSync("assets/images/华池岩岫.png"),
|
||||||
let successCount = 0;
|
solo: file.readImageMatSync("assets/images/单人挑战.png"),
|
||||||
let attemptCount = 0;
|
start: file.readImageMatSync("assets/images/开始挑战.png"),
|
||||||
|
leyline: file.readImageMatSync("assets/images/地脉异常.png"),
|
||||||
|
activate: file.readImageMatSync("assets/images/启动.png")
|
||||||
|
};
|
||||||
|
|
||||||
// 定义识别对象(使用相对路径)
|
// 模版匹配对象
|
||||||
const enterDungeonRo = RecognitionObject.TemplateMatch(
|
const ro = {
|
||||||
file.readImageMatSync("assets/images/华池岩岫.png")
|
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)
|
||||||
|
};
|
||||||
|
|
||||||
const soloChallengeRo = RecognitionObject.TemplateMatch(
|
// --- 照搬 123.js 的 OCR 对象逻辑 ---
|
||||||
file.readImageMatSync("assets/images/单人挑战.png")
|
// 识别全屏范围
|
||||||
);
|
fightOcrRo = RecognitionObject.Ocr(0, 0, genshin.width, genshin.height);
|
||||||
|
|
||||||
const startChallengeRo = RecognitionObject.TemplateMatch(
|
log.info("资源加载完成");
|
||||||
file.readImageMatSync("assets/images/开始挑战.png")
|
|
||||||
);
|
|
||||||
|
|
||||||
const leylineDisorderRo = RecognitionObject.TemplateMatch(
|
// 2. 准备工作
|
||||||
file.readImageMatSync("assets/images/地脉异常.png")
|
await genshin.returnMainUi();
|
||||||
);
|
if (settings.partyName) {
|
||||||
|
await genshin.switchParty(settings.partyName);
|
||||||
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;
|
// 3. 循环逻辑
|
||||||
}
|
const maxAttempts = settings.loopTimes || 15;
|
||||||
|
let successCount = 0;
|
||||||
|
|
||||||
while (attemptCount < maxAttempts) {
|
for (let i = 0; i < maxAttempts; i++) {
|
||||||
attemptCount++;
|
log.info(`=== 开始第 ${i + 1}/${maxAttempts} 次循环 ===`);
|
||||||
log.info(`开始第 ${attemptCount}/${maxAttempts} 次挑战`);
|
|
||||||
|
|
||||||
let stepSuccess = false;
|
// a. 传送
|
||||||
|
await genshin.tp("1436.2861328125", "1289.95556640625");
|
||||||
|
await sleep(2500);
|
||||||
|
|
||||||
// a. 传送至点位
|
// b. 寻找秘境入口
|
||||||
await genshin.tp("1436.2861328125", "1289.95556640625");
|
log.info("寻找秘境入口...");
|
||||||
log.info("已传送至目标点位");
|
keyDown("w");
|
||||||
await sleep(2000);
|
const foundEnter = await waitFindTemplate(ro.enter, 3000);
|
||||||
|
keyUp("w");
|
||||||
|
|
||||||
// b. 前进并检测"华池岩岫"
|
if (!foundEnter) {
|
||||||
keyDown("w");
|
log.warn("未找到秘境入口,重置位置");
|
||||||
const enterDungeonTimeout = 3000;
|
await genshin.returnMainUi();
|
||||||
const enterDungeonStartTime = Date.now();
|
continue;
|
||||||
let foundEnterDungeon = false;
|
}
|
||||||
|
|
||||||
while (Date.now() - enterDungeonStartTime < enterDungeonTimeout) {
|
log.info("找到入口");
|
||||||
const capture = captureGameRegion();
|
keyPress("f");
|
||||||
const result = capture.find(enterDungeonRo);
|
await sleep(1500);
|
||||||
capture.dispose();
|
|
||||||
|
|
||||||
if (!result.isEmpty()) {
|
// c. 点击单人挑战
|
||||||
foundEnterDungeon = true;
|
if (!await waitAndClickTemplate(ro.solo, 5000)) {
|
||||||
|
log.error("未找到'单人挑战',重试");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await sleep(1000);
|
||||||
|
|
||||||
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
await sleep(100);
|
|
||||||
|
// f. 走向钥匙并启动
|
||||||
|
log.info("走向启动钥匙...");
|
||||||
|
keyDown("w");
|
||||||
|
const foundActivate = await waitFindTemplate(ro.activate, 10000);
|
||||||
|
keyUp("w");
|
||||||
|
|
||||||
|
if (foundActivate) {
|
||||||
|
log.info("找到启动,开始战斗");
|
||||||
|
keyPress("f");
|
||||||
|
await sleep(1000);
|
||||||
|
|
||||||
|
// g. === 自动战斗 (完全复刻 123.js 的逻辑) ===
|
||||||
|
// 使用 SoloTask("AutoFight") + CancellationToken + 循环OCR检测
|
||||||
|
const fightResult = await autoFightLike123js(fightOcrRo, 120000);
|
||||||
|
|
||||||
|
if (fightResult) {
|
||||||
|
successCount++;
|
||||||
|
log.info(`战斗成功!当前完成 ${successCount} 次`);
|
||||||
|
await sleep(2000);
|
||||||
|
} else {
|
||||||
|
log.error("战斗失败,终止脚本");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.error("未找到启动钥匙");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keyUp("w");
|
await genshin.tp("1436.2861328125", "1289.95556640625");
|
||||||
|
log.info(`脚本结束。成功完成 ${successCount}/${maxAttempts} 次挑战`);
|
||||||
|
|
||||||
if (!foundEnterDungeon) {
|
} catch (e) {
|
||||||
log.warn("未找到'进入秘境',重试中...");
|
log.error("脚本运行出错: " + e.message);
|
||||||
await genshin.returnMainUi();
|
} finally {
|
||||||
continue;
|
// --- 资源释放 (保持内存安全) ---
|
||||||
|
for (let key in mats) {
|
||||||
|
if (mats[key]) mats[key].Dispose();
|
||||||
}
|
}
|
||||||
|
if (fightOcrRo && fightOcrRo.Dispose) {
|
||||||
log.info("已找到'进入秘境'");
|
fightOcrRo.Dispose();
|
||||||
await sleep(500);
|
|
||||||
|
|
||||||
// c. 按F键并检测"单人挑战"
|
|
||||||
keyPress("f");
|
|
||||||
await sleep(5000);
|
|
||||||
|
|
||||||
const foundSoloChallenge = await findAndClick(soloChallengeRo);
|
|
||||||
if (!foundSoloChallenge) {
|
|
||||||
log.error("未找到'单人挑战',终止脚本");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// f. 前进并检测"启动"
|
|
||||||
keyDown("w");
|
|
||||||
const activateTimeout = 10000;
|
|
||||||
const foundActivate = await findAndClick(activateRo, 999, 100, activateTimeout);
|
|
||||||
keyUp("w");
|
|
||||||
|
|
||||||
if (!foundActivate) {
|
|
||||||
log.error("未找到'启动',超时终止");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("已找到'启动'");
|
|
||||||
keyPress("f");
|
|
||||||
await sleep(1000);
|
|
||||||
|
|
||||||
// g. 开始自动战斗
|
|
||||||
log.info("开始自动战斗");
|
|
||||||
const fightResult = await autoFight(120000); // 120秒战斗超时
|
|
||||||
|
|
||||||
if (fightResult) {
|
|
||||||
successCount++;
|
|
||||||
log.info(`战斗成功!当前完成 ${successCount} 次`);
|
|
||||||
} else {
|
|
||||||
log.error("战斗失败,终止脚本");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
log.info("资源已释放");
|
||||||
}
|
}
|
||||||
|
|
||||||
await genshin.tp("1436.2861328125", "1289.95556640625");
|
// ================= 辅助函数 =================
|
||||||
log.info(`脚本结束。成功完成 ${successCount}/${attemptCount} 次挑战`);
|
|
||||||
|
|
||||||
// 自动战斗函数
|
/**
|
||||||
async function autoFight(timeout) {
|
* 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 {
|
||||||
fightResult = true;
|
// 使用传入的 ocrRo 进行查找
|
||||||
break;
|
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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
log.error(`OCR识别出错: ${err}`);
|
||||||
|
} finally {
|
||||||
|
// 123.js 缺少这一步,这里加上以保证长时间挂机不崩
|
||||||
|
capture.Dispose();
|
||||||
}
|
}
|
||||||
ro.dispose();
|
|
||||||
await sleep(1000);
|
await sleep(1000); // 123.js 的间隔是 1000ms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 停止战斗
|
||||||
cts.cancel();
|
cts.cancel();
|
||||||
return fightResult;
|
return fightResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 战斗文本识别函数
|
// 模版点击辅助函数 (内存安全版)
|
||||||
function recognizeFightText(captureRegion) {
|
async function waitAndClickTemplate(ro, timeoutMs) {
|
||||||
try {
|
const start = Date.now();
|
||||||
const result = captureRegion.find(ocrRo2);
|
while (Date.now() - start < timeoutMs) {
|
||||||
const text = result.text;
|
let capture = captureGameRegion();
|
||||||
const keywords = ["挑战成功", "达成", "挑战达成"];
|
try {
|
||||||
|
let res = capture.find(ro);
|
||||||
for (const keyword of keywords) {
|
if (!res.isEmpty()) {
|
||||||
if (text.includes(keyword)) {
|
res.click();
|
||||||
|
// 123.js 的逻辑是点两下,这里保留双击逻辑以防万一
|
||||||
|
await sleep(30);
|
||||||
|
res.click();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
capture.Dispose();
|
||||||
}
|
}
|
||||||
return false;
|
await sleep(1000); // 123.js 默认间隔较长,这里稍微对齐
|
||||||
} catch (error) {
|
|
||||||
log.error("OCR识别出错: {0}", error);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user