js:锄地和狗粮调整 (#2199)

* js:AAA狗粮批发1.4.0

更新识别用图和对应的识别区域
增加沉玉仙茗
增加支持其他拾取物
移除更新日志

* js:狗粮联机团购

调整截图区域和模板图片
移除更新日志
增加联机状态通知
增加狗粮分解

* js:狗粮和锄地

调整几张图片
This commit is contained in:
mno
2025-10-21 13:27:52 +08:00
committed by GitHub
parent 08781fc941
commit 0876930181
34 changed files with 239 additions and 183 deletions

View File

@@ -83,125 +83,5 @@ https://www.kdocs.cn/wo/sl/v13uXscL
- 2. 首次运行建议比前一天运行任何其他狗粮更晚,否则可能存在部分点位未刷新
- 3. 电脑性能较差,容易跑偏/跑过头,建议降低画质等,并关闭不必要的其他程序
- **这个js好慢不像abe四十分钟就跑完了**那就用abe
## 更新日志
### 1.3.122025.10.18
1. 修几个漏捡的点
2. 优化激活tp点的重复次数,加快速度
### 1.3.112025.10.12
1. 修几个漏捡的点
### 1.3.102025.10.11
1. 自定义配置中加入匹配阈值自定义
### 1.3.92025.10.6
1. 新增挪德卡莱狗粮点位
### 1.3.82025.10.2
1. 提高识别f图标和狗粮时的模板匹配阈值
### 1.3.72025.10.1
1. 修几个漏捡的点
### 1.3.62025.09.30
1. 更新readme
### 1.3.52025.09.30
1. 修几个漏捡的点
### 1.3.42025.09.29
1. 修几个漏捡的点
2. 大炮点合并到踏鞴砂收尾里
### 1.3.32025.09.27
1. 修几个漏捡的点
### 1.3.22025.09.26
1. 移除大部分点位的简易策略F.每条路线节约1~10秒.PS如果发现有漏捡调查点,记得录像发我
### 1.3.12025.09.22
1. 修正等待时间错误
### 1.3.02025.09.21
1. 将拾取方式修改为模板匹配拾取
### 1.2.152025.09.20
1. 修几条路线305①,632,640,628
### 1.2.142025.09.19
1. 修几条路线305③,622,632,628,638,629清怪,
### 1.2.132025.09.16
1. 修几条路线多人收尾区CD,
2. 新增304神无卓狸猫火炬
### 1.2.122025.09.15
1. 修几条路线628,05额外鸡鸡
### 1.2.112025.09.13
1. 修05额外-鸡鸡9
### 1.2.102025.09.12
1. 修几条路线305①③,622,623,632,214,628,635,003激活纳塔-鸡鸡,05额外-鸡鸡9,
### 1.2.92025.09.07
1. 修复额外等待时间不生效的问题
2. 修几条路线228,622,638,305【清怪】稻妻-踏鞴,
### 1.2.82025.09.04
1. 修几条路线305【清怪】稻妻-踏鞴砂,417,622,632,634,
2. 【额外】水天丛林时间改成12点,未解锁任务也可以降水位
### 1.2.72025.09.03
1. 修几条路线622,638,
2. 联机-收尾-清怪增加踏鞴砂
### 1.2.62025.09.03
1. 调整清怪和准备的位置
### 1.2.52025.09.02
1. 修几条路线428,642,度假村激活
### 1.2.32025.09.01
1. 修复了日期和时间计算的一个bug该bug曾导致每个月1号时断点续跑不生效
### 1.2.32025.08.31
1. 修几条路线640,642,激活路线度假村/智障厅
### 1.2.22025.08.30
1. 修几条路线303③,632,625.628,638,640,
2. 新增305【清怪】稻妻-踏鞴砂
### 1.2.12025.08.28
1. 修几条路线305,506,620,625,636
### 1.2.02025.08.28
1. 修正时间判断逻辑该bug曾导致北京时间400-800被视为前一天不刷新路线cd
2. 路线末坐标判断加入重试判断失败时等待1秒后重试
### 1.1.62025.08.28
1. 修几条路线414,501,506,623,632,640,642,643,06灵迷纹
2. 新增多人联机629清怪
### 1.1.52025.08.27
1. 修几条路线305②③,204,413,602,638
2. 修正mno传错的路径
### 1.1.42025.08.27
1. 尝试修正经验识别
### 1.1.32025.08.26
1. 更正联机收尾路线逻辑
### 1.1.22025.08.25
1. 优化几条卡脚路线
### 1.1.12025.08.25
1. 加入检测并点击过期物品弹窗
### 1.1.02025.08.24
1. 开始尝试适配联机狗粮
### 1.0.92025.08.23
1. 优化202,301,506,619,622,640,纳塔激活路线摔死
### 1.0.82025.08.22
1. 优化度假村几处卡脚
### 1.0.72025.08.22
1. 修复摧毁狗粮失败的问题
2. 修复路线完成校验失效的问题,现在能正常检测到路线完成失败,此时不会记录该路线完成
3. 优化部分路线
### 1.0.62025.08.22
1. 修复自动分解不生效的问题
2. 优化强制黑白芙逻辑
3. 优化部分路线
### 1.0.52025.08.21
1. 优化分解狗粮,提高容错
### 1.0.42025.08.20
1. 修改黑白芙切换逻辑
### 1.0.32025.08.20
1. 优化度假村路线
### 1.0.22025.08.20
1. 修复沿途自动分解不生效
### 1.0.12025.08.19
1. 正式版的第一次归我了
2. 优化几个度假村路线
3. 灵秘纹第一处调整
### 1.0.02025.08.19
1. 正式版上线
2. 优化分解狗粮与识别相关逻辑
3. 勾选通知后同时在摩拉识别和经验识别的界面截图发送通知
### 0.0.92025.08.18
1. 优化度假村若干路线
### 0.0.82025.08.18
1. 额外和收尾路线的激活和准备不再指定队伍
2. 移除路径中的切换黑白芙,改为换队伍时触发
### 0.0.72025.08.17
1. 加入纳塔度假村地区,重新规划富AB路线;并移除清怪路线节约时间
2. 富A富B的上限点98个完全不重叠,AB交替不再受12小时限制
### 0.0.62025.08.13
1. 修复"99强制黑芙"路线摔死bug
- **想要捡狗粮的时候也能捡其他东西** 去锄地一条龙js中获取相关物品图片放在本js的assets/targetitems中
- **想要测作者怎么办** 来q群1057307824测测莫酱

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1338,8 +1338,16 @@ async function recognizeAndInteract() {
let result;
let itemName = null;
for (const targetItem of targetItems) {
let recognitionObject = RecognitionObject.TemplateMatch(targetItem.template, 1219, centerYF - 15, 32 + 30 * (targetItem.itemName.length) + 2, 30);
recognitionObject.Threshold = TMthreshold;
//log.info(`正在尝试匹配${targetItem.itemName}`);
const cnLen = Math.min([...targetItem.itemName].filter(c => c >= '\u4e00' && c <= '\u9fff').length, 5);
const recognitionObject = RecognitionObject.TemplateMatch(
targetItem.template,
1219,
centerYF - 15,
12 + 28 * cnLen + 2,
30
);
recognitionObject.Threshold = targetItem.Threshold;
recognitionObject.InitTemplate();
result = gameRegion.find(recognitionObject);
if (result.isExist()) {

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "AAA狗粮批发",
"version": "1.3.12",
"version": "1.4.0",
"tags": [
"狗粮"
],

View File

@@ -22,55 +22,3 @@
* **为万叶佩戴生命主词条或武器**多人运行时队伍中只有一个角色一旦角色死亡将进入复苏界面bgi暂时没有对该界面进行处理
* **ocr识别失败**等待队员进入世界时会识别申请列表中的名字部分设备或环境可能ocr效果不好当你发现无法正常让队友进入时可以截图申请进入的界面参考js文件夹中的targets文件夹中图片截图并裁剪只剩下名字部分后放入该文件夹
* **预留足够的背包空间**运行AAA狗粮批发将获取约150个圣遗物运行本js将获取约230个圣遗物请确保你的背包有足够的空间容纳这些圣遗物建议在AAA狗粮批发中选择分解或摧毁并预留380+的空间
## 更新日志
### 1.4.112025.10.18
1. 修几个漏捡的点
### 1.4.102025.10.12
1. 修几个漏捡的点
### 1.4.92025.10.11
1. 将等待时的输出改为10秒一次
2. 将调试模式开关移到自定义配置顶部
3. 新增自定义配置,模板匹配阈值
### 1.4.82025.10.6
1. 把踏鞴砂大炮点调整至联机里拾取
### 1.4.72025.10.2
1. 提高模板匹配阈值
### 1.4.62025.10.1
1. 修几个漏捡的点
### 1.4.52025.09.30
1. 捕获识别过程未知来源的报错防止直接终止
### 1.4.42025.09.30
1. 修几个漏捡的点
### 1.4.32025.09.29
1. 修几个漏捡的点
2. 大炮点合并到踏鞴砂收尾里
### 1.4.22025.09.27
1. 修几个漏捡的点
### 1.4.12025.09.26
1. 移除大部分点位的简易策略F.每条路线节约1~10秒.PS如果发现有漏捡调查点,记得录像发我
### 1.4.02025.09.20
1. 将拾取模式修改为模板匹配拾取
### 1.3.22025.09.20
1. 修几处路线度假村踩点,624③,509①②,305①
### 1.3.12025.09.19
1. 修几处路线624②③,629,426,509③,304,305③
### 1.3.02025.09.17
1. 增加识别并点击复苏
### 1.2.72025.09.16
1. 修几处路线624①②③,509①
2. 新增304神无卓狸猫火炬
### 1.2.62025.09.15
1. 修几处路线624①②③,509①③,05额外鸡屁股
### 1.2.52025.09.13
1. 修05额外鸡屁股
### 1.2.42025.09.12
1. 修几处路线509②,305①③,04额外瓶子,05额外鸡屁股
### 1.2.32025.09.08
1. 更新了一处bug
### 1.2.22025.09.07
1. 修几处路线304,624①②,626,627,426,509①②,
### 1.2.12025.09.04
1. 修几处路线627,水天丛林,
### 1.2.02025.09.03
1. 增加几处错误处理,增加容错

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -12,6 +12,9 @@ let TMthreshold = +settings.TMthreshold || 0.9;
(async function () {
setGameMetrics(1920, 1080, 1);
if (settings.logName) {
await processArtifacts();
}
await genshin.tpToStatueOfTheSeven();
await switchPartyIfNeeded(settings.partyName);
targetItems = await loadTargetItems();
@@ -67,7 +70,8 @@ let TMthreshold = +settings.TMthreshold || 0.9;
};
Object.assign(autoEnterSettings, permits);
log.info(`等待他人进入自己世界,白名单人数:${autoEnterSettings.maxEnterCount}`);
log.info(`等待他人进入自己世界,目标人数:${autoEnterSettings.maxEnterCount}`);
notification.send(`等待他人进入自己世界,目标人数:${autoEnterSettings.maxEnterCount}`);
} else {
// 构造队员配置
autoEnterSettings = {
@@ -76,6 +80,7 @@ let TMthreshold = +settings.TMthreshold || 0.9;
timeout: 5
};
log.info(`将要进入序号${idx}uid为${settings[`p${idx}UID`]}的世界`);
notification.send(`将要进入序号${idx}uid为${settings[`p${idx}UID`]},名称为${settings[`p${idx}Name`]}的世界`);
}
let attempts = 0;
while (attempts < 5) {
@@ -84,6 +89,7 @@ let TMthreshold = +settings.TMthreshold || 0.9;
//队员加入后要检查房主名称
if (autoEnterSettings.enterMode === "进入他人世界" && attempts != 5) {
if (await checkP1Name(settings[`p${idx}Name`])) {
notification.send(`成功进入序号${idx}uid为${settings[`p${idx}UID`]},名称为${settings[`p${idx}Name`]}的世界`);
break;
} else {
//进入了错误的世界,退出世界并重新加入,最后一次不检查
@@ -114,6 +120,12 @@ let TMthreshold = +settings.TMthreshold || 0.9;
}
}
await genshin.tpToStatueOfTheSeven();
if (settings.logName) {
let expGain = await processArtifacts();
log.info(`${settings.logName}:联机狗粮分解获得经验${expGain}`);
notification.send(`${settings.logName}:联机狗粮分解获得经验${expGain}`);
}
}
)();
@@ -514,13 +526,23 @@ async function runGroupPurchasing(runExtra) {
if (files.length === 0) {
log.warn(`文件夹 ${folderPath} 下未找到任何 JSON 路线文件`);
return;
}
for (const { fullPath } of files) {
await runPath(fullPath, 1);
}
try {
const pos = await genshin.getPositionFromMap();
const dist = Math.hypot(pos.x - 2297.3, pos.y + 823.8);
if (dist && dist < 100) {
log.warn("仍然在七天神像附近,尝试重跑至多一次");
for (const { fullPath } of files) {
await runPath(fullPath, 1);
}
}
} catch (error) {
}
log.info(`${folderName} 的全部路线已完成`);
}
}
@@ -644,6 +666,7 @@ async function autoEnter(autoEnterSettings) {
enterCount++;
enteredPlayers = [...new Set([...enteredPlayers, result])];
log.info(`允许 ${result} 加入`);
notification.send(`允许 ${result} 加入`);
if (await isYUI()) { keyPress("VK_ESCAPE"); await sleep(500); await genshin.returnMainUi(); }
break;
} else {
@@ -659,12 +682,20 @@ async function autoEnter(autoEnterSettings) {
if (enterCount >= maxEnterCount || checkToEnd) {
checkToEnd = true;
await sleep(20000);
if (await findTotalNumber() === maxEnterCount + 1) break;
if (await findTotalNumber() === maxEnterCount + 1) {
notification.send(`已达到预定人数:${maxEnterCount + 1}`);
break;
}
else enterCount--;
}
}
}
if (new Date() - start >= timeout * 60 * 1000) {
log.warn("超时未达到预定人数");
notification.error(`超时未达到预定人数`);
}
async function confirmSearchResult() {
for (let i = 0; i < 4; i++) {
const gameRegion = captureGameRegion();
@@ -1146,8 +1177,16 @@ async function recognizeAndInteract() {
let result;
let itemName = null;
for (const targetItem of targetItems) {
let recognitionObject = RecognitionObject.TemplateMatch(targetItem.template, 1219, centerYF - 15, 32 + 30 * (targetItem.itemName.length) + 2, 30);
recognitionObject.Threshold = TMthreshold;
//log.info(`正在尝试匹配${targetItem.itemName}`);
const cnLen = Math.min([...targetItem.itemName].filter(c => c >= '\u4e00' && c <= '\u9fff').length, 5);
const recognitionObject = RecognitionObject.TemplateMatch(
targetItem.template,
1219,
centerYF - 15,
12 + 28 * cnLen + 2,
30
);
recognitionObject.Threshold = targetItem.Threshold;
recognitionObject.InitTemplate();
result = gameRegion.find(recognitionObject);
if (result.isExist()) {
@@ -1200,3 +1239,179 @@ async function recognizeAndInteract() {
return false;
}
}
async function processArtifacts() {
// 定义一个独立的函数用于在指定区域进行 OCR 识别并输出识别内容
async function recognizeTextInRegion(ocrRegion, timeout = 5000) {
let startTime = Date.now();
let retryCount = 0; // 重试计数
while (Date.now() - startTime < timeout) {
try {
// 在指定区域进行 OCR 识别
const gameRegion = captureGameRegion();
let ocrResult = gameRegion.find(RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height));
gameRegion.dispose();
if (ocrResult) {
let correctedText = ocrResult.text;
return correctedText; // 返回识别到的内容
} else {
log.warn(`OCR 识别区域未找到内容`);
return null; // 如果 OCR 未识别到内容,返回 null
}
} catch (error) {
retryCount++; // 增加重试计数
log.warn(`OCR 识别失败,正在进行第 ${retryCount} 次重试...`);
}
await sleep(500); // 短暂延迟,避免过快循环
}
log.warn(`经过多次尝试,仍然无法在指定区域识别到文字`);
return null; // 如果未识别到文字,返回 null
}
const decomposeRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/decompose.png"));
const quickChooseRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/quickChoose.png"));
const confirmRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/confirm.png"));
const doDecomposeRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/doDecompose.png"));
const doDecompose2Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/doDecompose2.png"));
const outDatedRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/ConfirmButton.png"), 760, 700, 100, 100);
await genshin.returnMainUi();
await sleep(500);
let result = 0;
try {
result = await decomposeArtifacts();
} catch (error) {
log.error(`处理狗粮分解时发生异常: ${error.message}`);
}
await genshin.returnMainUi();
return result;
async function decomposeArtifacts() {
keyPress("B");
if (await findAndClick(outDatedRo)) {
log.info("检测到过期物品弹窗,处理");
await sleep(1000);
}
await sleep(1000);
await click(670, 45);
await sleep(500);
if (!await findAndClick(decomposeRo)) {
await genshin.returnMainUi();
return 0;
}
await sleep(1000);
// 识别已储存经验1570-880-1650-930
const regionToCheck1 = { x: 1570, y: 880, width: 80, height: 50 };
const raw = await recognizeTextInRegion(regionToCheck1);
// 把识别到的文字里所有非数字字符去掉,只保留数字
const digits = (raw || '').replace(/\D/g, '');
let initialValue = 0;
if (digits) {
initialValue = parseInt(digits, 10);
log.info(`已储存经验识别成功: ${initialValue}`);
} else {
log.warn(`在指定区域未识别到有效数字: ${initialValue}`);
}
let regionToCheck3 = { x: 100, y: 885, width: 170, height: 50 };
if (!await findAndClick(quickChooseRo)) {
await genshin.returnMainUi();
return 0;
}
moveMouseTo(960, 540);
await sleep(1000);
// 点击“确认选择”按钮
if (!await findAndClick(confirmRo)) {
await genshin.returnMainUi();
return 0;
}
await sleep(1500);
let decomposedNum2 = await recognizeTextInRegion(regionToCheck3);
// 使用正则表达式提取第一个数字
const match2 = decomposedNum2.match(/已选(\d+)/);
// 检查是否匹配成功
if (match2) {
// 将匹配到的第一个数字转换为数字类型并存储在变量中
let firstNumber2 = Number(match2[1]);
log.info(`分解总数是: ${firstNumber2}`);
} else {
log.info("识别失败");
}
//识别当前总经验
notification.Send(`当前经验如图`);
// 当前总经验1470-880-205-70
const regionToCheck2 = { x: 1470, y: 880, width: 205, height: 70 };
const raw2 = await recognizeTextInRegion(regionToCheck2);
// 只保留数字
const digits2 = (raw2 || '').replace(/\D/g, '');
let newValue = 0;
if (digits2) {
newValue = parseInt(digits2, 10);
log.info(`当前总经验识别成功: ${newValue}`);
} else {
log.warn(`在指定区域未识别到有效数字: ${newValue}`);
}
log.info(`用户选择了分解,执行分解`);
// 根据用户配置,分解狗粮
await sleep(1000);
// 点击分解按钮
if (!await findAndClick(doDecomposeRo)) {
await genshin.returnMainUi();
return 0;
}
await sleep(500);
// 4. "进行分解"按钮// 点击进行分解按钮
if (!await findAndClick(doDecompose2Ro)) {
await genshin.returnMainUi();
return 0;
}
await sleep(1000);
// 5. 关闭确认界面
await click(1340, 755);
await sleep(1000);
const resinExperience = Math.max(newValue - initialValue, 0);
log.info(`分解可获得经验: ${resinExperience}`);
let resultExperience = resinExperience;
if (resultExperience === 0) {
resultExperience = initialValue;
}
const result = resultExperience;
await genshin.returnMainUi();
return result;
}
async function findAndClick(target, maxAttempts = 20) {
for (let attempts = 0; attempts < maxAttempts; attempts++) {
const gameRegion = captureGameRegion();
try {
const result = gameRegion.find(target);
if (result.isExist) {
result.click();
return true; // 成功立刻返回
}
log.warn(`识别失败,第 ${attempts + 1} 次重试`);
} catch (err) {
} finally {
gameRegion.dispose();
}
if (attempts < maxAttempts - 1) { // 最后一次不再 sleep
await sleep(250);
}
}
return false;
}
}

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "AAA狗粮联机团购",
"version": "1.4.11",
"version": "1.5.1",
"tags": [
"狗粮"
],

View File

@@ -9,6 +9,11 @@
"type": "checkbox",
"label": "调试时勾选,跳过路线执行逻辑"
},
{
"name": "logName",
"type": "input-text",
"label": "填写后将在js运行结束后分解狗粮\n并输出分解获得的经验值\n格式为 名字联机狗粮分解获得经验xxx"
},
{
"name": "groupMode",
"type": "select",

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB