js:锄地一条龙 清理爆仓材料 (#2862)

* js:锄地一条龙

高收益优先模式的菜肴排序依据也改为文件名

* js:清理爆仓材料

支持删除材料界面的材料,允许设置白名单等以排除部分材料不删
长按过程中增加鼠标移动以防止有人跑一半乱动鼠标
增加终止快捷键按下时的跳出点
This commit is contained in:
mno
2026-02-11 16:18:21 +08:00
committed by GitHub
parent 7c9fe842d6
commit 6c0088e9e0
13 changed files with 158 additions and 84 deletions

View File

@@ -1,3 +1,5 @@
1. 适配1080p分辨率其他分辨率不能使用属于正常现象能够使用纯属巧合
2. 目前只支持删除养成道具页的1-2星材料
3. 注意当你的流浪者的经验(绿色经验书)数量大于你设置的起删数量时,也会删除
2. 目前只支持删除材料和养成道具页的0-2星材料
3. 注意当你的流浪者的经验(绿色经验书)数量大于你设置的起删数量并小于设置的终止数量,且不等于指定的白名单数量时,也会删除
4. 务必仔细自行关注各个物品的数量,如有误删,后果自负
5. 将js添加至配置组中在配置组界面右键点击js以修改自定义配置

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1,11 +1,14 @@
const bottomRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/到底了.png"), 1282, 934, 1296 - 1282, 945 - 934);
bottomRo.Threshold = 0.9;
bottomRo.InitTemplate();
const star1Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/星.png"), 107, 214, 1169, 706);
star1Ro.Threshold = 0.9;
const star0Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/0星.png"), 107, 214, 1169, 706);
star0Ro.Threshold = 0.93;
star0Ro.InitTemplate();
const star1Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/1星.png"), 107, 214, 1169, 706);
star1Ro.Threshold = 0.85;
star1Ro.InitTemplate();
const star2Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/星.png"), 107, 214, 1169, 706);
star2Ro.Threshold = 0.9;
const star2Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/2星.png"), 107, 214, 1169, 706);
star2Ro.Threshold = 0.85;
star2Ro.InitTemplate();
const delete1Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/摧毁图标1.png"), 31, 969, 93, 101);
delete1Ro.Threshold = 0.9;
@@ -29,102 +32,140 @@ let scrollScale = Number(settings.scrollScale) || 10;
(async function () {
//进入养成道具界面
if (!settings.delayScale) {
log.error("延迟系数异常,你从来没打开过自定义配置");
log.error("去阅读README后再使用");
return;
}
log.warn("务必仔细自行关注各个物品的数量,如有误删,后果自负");
await genshin.returnMainUi();
log.warn("务必仔细自行关注各个物品的数量,如有误删,后果自负");
await genshin.tpToStatueOfTheSeven();
log.warn("务必仔细自行关注各个物品的数量,如有误删,后果自负");
await genshin.returnMainUi();
keyPress("B");
let type = "养成道具";
let types = Array.from(settings.executingTypes);
const targetNumber = Number(settings.targetNumber) || 9000;
const startNumber = Number(settings.startNumber) || 9900;
const endNumber = Number(settings.endNumber) || 9999;
const whiteNumbers = settings.whiteNumbers || "";
// 按中文逗号分隔,去除空项
const numberArray = whiteNumbers
.split('')
.map(s => s.trim())
.filter(s => s !== '');
const validWhiteNumbers = [];
// 校验每一项是否为有效数字
for (const item of numberArray) {
const num = Number(item);
// 校验必须是有效数字不是NaN不是Infinity
if (!Number.isFinite(num)) {
log.error(`${item} 不是有效数字`);
throw new Error('存在非法数字格式');
}
validWhiteNumbers.push(num);
}
// 此时 validWhiteNumbers 存储的是数字类型数组,如 [1, 2, 3]
log.info(`白名单数字列表:${validWhiteNumbers.join(', ')}`);
if (targetNumber >= startNumber) {
log.error("目标数量必须小于起删数量");
return;
}
if (await findAndClick([`assets/RecognitionObject/背包界面/${type}1.png`, `assets/RecognitionObject/背包界面/${type}2.png`])) {
log.info(`成功进入${type}界面,开始执行`);
} else {
await genshin.returnMainUi();
keyPress("B");
for (let type of types) {
try { await sleep(1) } catch (e) { break; }
if (await findAndClick([`assets/RecognitionObject/背包界面/${type}1.png`, `assets/RecognitionObject/背包界面/${type}2.png`])) {
log.info(`成功进入${type}界面,开始执行`);
} else {
log.info(`进入${type}界面失败`);
return;
}
}
let scrolls = 0
while (scrolls < 200) {
try { await sleep(1) } catch (e) { break; }
if (await findAndClick(delete2Ro, false, 100, 16)) {
await findAndClick("assets/RecognitionObject/返回.png");
await sleep(delay2);
}
let findFullRes = null;
let time4 = new Date();
const gameRegion = captureGameRegion();
const allRes1 = gameRegion.findMulti(star2Ro);
let time5 = new Date();
log.info(`调试-找出所有2星用时${time5 - time4}`);
for (let i = 0; i < allRes1.count; i++) {
const res = allRes1[i];
const num = await numberTemplateMatch("assets/背包物品数字", res.x + 34, res.y + 16, 59, 30);
if (num >= startNumber) {
findFullRes = { x: res.x, y: res.y };
break;
await genshin.returnMainUi();
keyPress("B");
if (await findAndClick([`assets/RecognitionObject/背包界面/${type}1.png`, `assets/RecognitionObject/背包界面/${type}2.png`])) {
log.info(`成功进入${type}界面,开始执行`);
} else {
log.info(`进入${type}界面失败`);
return;
}
}
let time6 = new Date();
log.info(`调试-依次核对数量${time6 - time5}`);
if (!findFullRes) {
let time1 = new Date();
const allRes2 = gameRegion.findMulti(star1Ro);
let time2 = new Date();
log.info(`调试-找出所有1星用时${time2 - time1}`);
for (let i = 0; i < allRes2.count; i++) {
const res = allRes2[i];
const num = await numberTemplateMatch("assets/背包物品数字", res.x + 34, res.y + 16, 59, 30);
if (num >= startNumber) {
await sleep(500);
let scrolls = 0
while (scrolls < 200) {
try { await sleep(1) } catch (e) { break; }
if (await findAndClick(delete2Ro, false, 100, 16)) {
await findAndClick("assets/RecognitionObject/返回.png");
await sleep(delay2);
}
let findFullRes = null;
const gameRegion = captureGameRegion();
const allRes1 = gameRegion.findMulti(star2Ro);
for (let i = 0; i < allRes1.count; i++) {
const res = allRes1[i];
const num = await numberTemplateMatch("assets/背包物品数字", res.x + 20, res.y + 14, 80, 30);
if (num >= startNumber && num <= endNumber && !validWhiteNumbers.includes(num)) {
findFullRes = { x: res.x, y: res.y };
break;
}
}
let time3 = new Date();
log.info(`调试-依次核对数量${time3 - time2}`);
}
gameRegion.dispose();
if (findFullRes) {
//二次确认
let fullNum = await numberTemplateMatch("assets/背包物品数字", findFullRes.x + 34, findFullRes.y + 16, 59, 30);
log.info(`找到一个爆仓材料,位置(${findFullRes.x},${findFullRes.y}),数量 ${fullNum}`);
await findAndClick(delete1Ro, true, 200, 16);
await sleep(delay2);
click(findFullRes.x + 20, findFullRes.y - 60);
await sleep(delay2);
if (!await deleteToTargetNumber(fullNum - targetNumber)) {
if (!findFullRes) {
const allRes2 = gameRegion.findMulti(star1Ro);
for (let i = 0; i < allRes2.count; i++) {
const res = allRes2[i];
const num = await numberTemplateMatch("assets/背包物品数字", res.x + 20, res.y + 14, 80, 30);
if (num >= startNumber && num <= endNumber && !validWhiteNumbers.includes(num)) {
findFullRes = { x: res.x, y: res.y };
break;
}
}
}
if (!findFullRes) {
const allRes3 = gameRegion.findMulti(star0Ro);
for (let i = 0; i < allRes3.count; i++) {
const res = allRes3[i];
const num = await numberTemplateMatch("assets/背包物品数字", res.x + 20, res.y + 14, 80, 30);
if (num >= startNumber && num <= endNumber && !validWhiteNumbers.includes(num)) {
findFullRes = { x: res.x, y: res.y };
break;
}
}
}
gameRegion.dispose();
if (findFullRes) {
let fullNum = await numberTemplateMatch("assets/背包物品数字", findFullRes.x + 20, findFullRes.y + 14, 80, 30);
log.info(`找到一个爆仓材料,位置(${findFullRes.x},${findFullRes.y}),数量 ${fullNum}`);
await findAndClick(delete1Ro, true, 200, 16);
await sleep(delay2);
click(findFullRes.x + 20, findFullRes.y - 60);
await sleep(delay2);
if (!await deleteToTargetNumber(fullNum - targetNumber)) {
continue;
}
await sleep(delay2);
await findAndClick(delete2Ro);
await sleep(delay2);
await findAndClick(delete3Ro);
itemCount++;
itemDeleteCount += fullNum - targetNumber;
continue;
}
await sleep(delay2);
await findAndClick(delete2Ro);
await sleep(delay2);
await findAndClick(delete3Ro);
itemCount++;
itemDeleteCount += fullNum - targetNumber;
continue;
}
scrolls++;
let bottomres = await findAndClick(bottomRo, false, 2, 3, 1);
if (bottomres) {
scrolls++;
let bottomres = await findAndClick(bottomRo, false, 2, 3, 1);
if (bottomres) {
moveMouseTo(139, 910);
await scrollDown(3);
bottomres = await findAndClick(bottomRo, false, 2, 3, 1);
if (bottomres) {
log.info(`到底了,${type}类型处理完毕`);
break;
}
}
moveMouseTo(139, 910);
await scrollDown(3);
bottomres = await findAndClick(bottomRo, false, 2, 3, 1);
if (bottomres) {
log.info(`到底了,${type}类型处理完毕`);
break;
}
}
moveMouseTo(139, 910);
await scrollDown(3);
}
log.info(`删除了${itemCount}种爆仓材料共${itemDeleteCount}`);
})();
@@ -205,6 +246,7 @@ async function findAndClick(target,
async function scrollDown(lines = 1) {
lines = lines * scrollScale;
for (let i = 0; i < lines; i++) {
try { await sleep(1) } catch (e) { break; }
await keyMouseScript.runFile(`assets/滚轮下翻.json`);
}
await sleep(delay1);
@@ -357,6 +399,7 @@ async function deleteToTargetNumber(delta) {
click(342, 923);
await sleep(delay1);
while (true) {
try { await sleep(1) } catch (e) { break; }
let res = await numberTemplateMatch("assets/摧毁物品数字", 192, 901, 116, 56);
//log.info(`调试-识别结果为${res}`);
if (res === -1 || Math.abs(res - lastRes) > 100) {
@@ -390,6 +433,9 @@ async function deleteToTargetNumber(delta) {
moveMouseTo(342, 923);
leftButtonDown();
state = 1;
} else {
moveMouseTo(342, 923);
leftButtonDown();
}
} else {
if (state != 0) {
@@ -410,6 +456,9 @@ async function deleteToTargetNumber(delta) {
moveMouseTo(168, 923);
leftButtonDown();
state = -1;
} else {
moveMouseTo(168, 923);
leftButtonDown();
}
} else {
if (state != 0) {

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "清理爆仓材料",
"version": "1.0",
"version": "1.1",
"description": "识别达到一定数量的材料,将其删除到指定数量",
"authors": [
{

View File

@@ -3,13 +3,36 @@
"name": "targetNumber",
"type": "input-text",
"label": "删除后的目标数量",
"default": "9000"
"default": "9900"
},
{
"name": "startNumber",
"type": "input-text",
"label": "起删数量,大于这个数量的材料才删",
"default": "9900"
"label": "起删数量,大于等于这个数量的材料才删",
"default": "9901"
},
{
"name": "endNumber",
"type": "input-text",
"label": "终止数量,大于这个数量的材料不删(以防把矿删了)",
"default": "9999"
},
{
"name": "whiteNumbers",
"type": "input-text",
"label": "白名单数量,数量恰好等于该值的物品不会被删除,如有多个,使用【中文逗号】分隔\n用于保留特定物品"
},
{
"name": "executingTypes",
"type": "multi-checkbox",
"label": "要执行的物品种类",
"default": [
"养成道具"
],
"options": [
"养成道具",
"材料"
]
},
{
"name": "delayScale",

View File

@@ -628,7 +628,7 @@ async function findBestRouteGroups(pathings, k1, k2, targetEliteNum, targetMonst
pathings.sort((a, b) => {
const aHigh = a.tags.includes("高收益") ? 1 : 0;
const bHigh = b.tags.includes("高收益") ? 1 : 0;
return bHigh - aHigh || a.index - b.index; // 有标签的在前,同标签按原顺
return bHigh - aHigh || a.fileName.localeCompare(b.fileName); // 有标签的在前,同标签按文件名排
});
break;

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "锄地一条龙",
"version": "2.2.7",
"version": "2.2.8",
"description": "一站式解决自动化锄地支持只拾取狗粮请仔细阅读README.md后使用",
"authors": [
{