diff --git a/repo/js/DestroyArtifactsForMora/Assets/RecognitionObject/Mora.png b/repo/js/DestroyArtifactsForMora/Assets/RecognitionObject/Mora.png new file mode 100644 index 000000000..4b5a6a3f6 Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/RecognitionObject/Mora.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/RecognitionObject/贵重物品.png b/repo/js/DestroyArtifactsForMora/Assets/RecognitionObject/贵重物品.png new file mode 100644 index 000000000..978a6514b Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/RecognitionObject/贵重物品.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/RecognitionObject/贵重物品2.png b/repo/js/DestroyArtifactsForMora/Assets/RecognitionObject/贵重物品2.png new file mode 100644 index 000000000..8b7c7b854 Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/RecognitionObject/贵重物品2.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/0.png b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/0.png new file mode 100644 index 000000000..3e08bf713 Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/0.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/1.png b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/1.png new file mode 100644 index 000000000..92f4e86c4 Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/1.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/2.png b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/2.png new file mode 100644 index 000000000..562b3bf89 Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/2.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/3.png b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/3.png new file mode 100644 index 000000000..4857790fe Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/3.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/4.png b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/4.png new file mode 100644 index 000000000..af92f713c Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/4.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/5.png b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/5.png new file mode 100644 index 000000000..ca565e88d Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/5.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/6.png b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/6.png new file mode 100644 index 000000000..1e2dd27ff Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/6.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/7.png b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/7.png new file mode 100644 index 000000000..de222721b Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/7.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/8.png b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/8.png new file mode 100644 index 000000000..aa4608259 Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/8.png differ diff --git a/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/9.png b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/9.png new file mode 100644 index 000000000..8a4cac4d7 Binary files /dev/null and b/repo/js/DestroyArtifactsForMora/Assets/背包摩拉数字/9.png differ diff --git a/repo/js/DestroyArtifactsForMora/main.js b/repo/js/DestroyArtifactsForMora/main.js index 71df21a04..e18df3bf8 100644 --- a/repo/js/DestroyArtifactsForMora/main.js +++ b/repo/js/DestroyArtifactsForMora/main.js @@ -10,6 +10,11 @@ const MidDestoryButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync */ (async function () { + await genshin.returnMainUi(); + let mora1 = 0; + if (settings.moraDiff) { + mora1 = await canCanNeed(); + } await genshin.returnMainUi(); keyPress("B"); await sleep(1500); @@ -80,4 +85,189 @@ const MidDestoryButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync await genshin.returnMainUi(); } -})(); \ No newline at end of file + let mora2 = 0; + if (settings.moraDiff) { + mora2 = await canCanNeed(); + let moraDiff = mora2 - mora1; + log.info(`分解获得摩拉数量:${moraDiff}`) + notification.send(`分解获得摩拉数量:${moraDiff}`); + } + +})(); + +async function canCanNeed() { + let tryTimes = 0; + let moraRes = -1; + while ((tryTimes < 2) && (moraRes < 0)) { + await genshin.returnMainUi(); + await sleep(100); + keyPress("B"); + + await sleep(1000); + //切换到贵重物品 + const gzwpRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/贵重物品.png")); + const gzwpRo2 = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/贵重物品2.png")); + let trys = 0; + while (trys < 10) { + trys++ + let res1 = await findAndClick(gzwpRo, 1); + let res2 = await findAndClick(gzwpRo2, 2); + if (res1 || res2) { + break; + } + } + await sleep(1000); + if (moraRes < 0) { + const moraRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/Mora.png"), 0, 970, 600, 1080 - 970); + const gameRegion = captureGameRegion(); + let moraX = 336; + let moraY = 1004; + try { + const result = gameRegion.find(moraRo); + if (result.isExist()) { + moraX = result.x; + moraY = result.y; + } + } catch (err) { + } finally { + gameRegion.dispose(); + } + let attempts = 0; + while (moraRes < 0 && attempts < 5) { + attempts++; + moraRes = await numberTemplateMatch("Assets/背包摩拉数字", moraX, moraY, 300, 40, 0.95, 0.85, 10); + } + if (moraRes >= 0) { + log.info(`成功识别到摩拉数值: ${moraRes}`); + } else { + log.warn("未能识别到摩拉数值。"); + } + } + await sleep(500); + tryTimes++; + } + return moraRes; +} + +/** + * 在指定区域内,用 0-9 的 PNG 模板做「多阈值 + 非极大抑制」数字识别, + * 最终把检测到的数字按左右顺序拼成一个整数返回。 + * + * @param {string} numberPngFilePath - 存放 0.png ~ 9.png 的文件夹路径(不含文件名) + * @param {number} x - 待识别区域的左上角 x 坐标,默认 0 + * @param {number} y - 待识别区域的左上角 y 坐标,默认 0 + * @param {number} w - 待识别区域的宽度,默认 1920 + * @param {number} h - 待识别区域的高度,默认 1080 + * @param {number} maxThreshold - 模板匹配起始阈值,默认 0.95(最高可信度) + * @param {number} minThreshold - 模板匹配最低阈值,默认 0.8(最低可信度) + * @param {number} splitCount - 在 maxThreshold 与 minThreshold 之间做几次等间隔阈值递减,默认 3 + * @param {number} maxOverlap - 非极大抑制时允许的最大重叠像素,默认 2;只要 x 或 y 方向重叠大于该值即视为重复框 + * + * @returns {number} 识别出的整数;若没有任何有效数字框则返回 -1 + * + * @example + * const mora = await numberTemplateMatch('摩拉数字', 860, 70, 200, 40); + * if (mora >= 0) console.log(`当前摩拉:${mora}`); + */ +async function numberTemplateMatch( + numberPngFilePath, + x = 0, y = 0, w = 1920, h = 1080, + maxThreshold = 0.95, + minThreshold = 0.87, + splitCount = 10, + maxOverlap = 2 +) { + let ros = []; + for (let i = 0; i <= 9; i++) { + ros[i] = RecognitionObject.TemplateMatch( + file.ReadImageMatSync(`${numberPngFilePath}/${i}.png`), x, y, w, h); + } + + function setThreshold(roArr, newThreshold) { + for (let i = 0; i < roArr.length; i++) { + roArr[i].Threshold = newThreshold; + roArr[i].InitTemplate(); + } + } + + const gameRegion = captureGameRegion(); + const allCandidates = []; + + /* 1. splitCount 次等间隔阈值递减 */ + for (let k = 0; k < splitCount; k++) { + const curThr = maxThreshold - (maxThreshold - minThreshold) * k / Math.max(splitCount - 1, 1); + setThreshold(ros, curThr); + + /* 2. 0-9 每个模板跑一遍,所有框都收 */ + for (let digit = 0; digit <= 9; digit++) { + const res = gameRegion.findMulti(ros[digit]); + if (res.count === 0) continue; + + for (let i = 0; i < res.count; i++) { + const box = res[i]; + allCandidates.push({ + digit: digit, + x: box.x, + y: box.y, + w: box.width, + h: box.height, + thr: curThr + }); + } + } + + } + gameRegion.dispose(); + + /* 3. 无结果提前返回 -1 */ + if (allCandidates.length === 0) { + return -1; + } + + /* 4. 非极大抑制(必须 x、y 两个方向重叠都 > maxOverlap 才视为重复) */ + const adopted = []; + for (const c of allCandidates) { + let overlap = false; + for (const a of adopted) { + const xOverlap = Math.max(0, Math.min(c.x + c.w, a.x + a.w) - Math.max(c.x, a.x)); + const yOverlap = Math.max(0, Math.min(c.y + c.h, a.y + a.h) - Math.max(c.y, a.y)); + if (xOverlap > maxOverlap && yOverlap > maxOverlap) { + overlap = true; + break; + } + } + if (!overlap) { + adopted.push(c); + //log.info(`在 [${c.x},${c.y},${c.w},${c.h}] 找到数字 ${c.digit},匹配阈值=${c.thr}`); + } + } + + /* 5. 按 x 排序,拼整数;仍无有效框时返回 -1 */ + if (adopted.length === 0) return -1; + adopted.sort((a, b) => a.x - b.x); + + return adopted.reduce((num, item) => num * 10 + item.digit, 0); +} + +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) { + await sleep(50); + result.click(); + await sleep(50); + return true; // 成功立刻返回 + } + log.warn(`识别失败,第 ${attempts + 1} 次重试`); + } catch (err) { + } finally { + gameRegion.dispose(); + } + if (attempts < maxAttempts - 1) { // 最后一次不再 sleep + await sleep(250); + } + } + return false; +} \ No newline at end of file diff --git a/repo/js/DestroyArtifactsForMora/manifest.json b/repo/js/DestroyArtifactsForMora/manifest.json index 28c06be08..335281e31 100644 --- a/repo/js/DestroyArtifactsForMora/manifest.json +++ b/repo/js/DestroyArtifactsForMora/manifest.json @@ -1,15 +1,19 @@ { "manifest_version": 1, "name": "摧毁圣遗物换摩拉", - "version": "1.2", + "version": "1.3", "bgi_version": "0.44.6", "description": "图像识别改造版", "authors": [ { "name": "起个名字好难的喵", - "links":"https://github.com/MisakaAldrich" + "links": "https://github.com/MisakaAldrich" + }, + { + "name": "mno", + "links": "https://github.com/Bedrockx" } ], "main": "main.js", "settings_ui": "settings.json" -} +} \ No newline at end of file diff --git a/repo/js/DestroyArtifactsForMora/settings.json b/repo/js/DestroyArtifactsForMora/settings.json index b2e7e0863..2aa36ee23 100644 --- a/repo/js/DestroyArtifactsForMora/settings.json +++ b/repo/js/DestroyArtifactsForMora/settings.json @@ -23,5 +23,11 @@ "name": "fourStar", "type": "checkbox", "label": "摧毁四星圣遗物" + }, + { + "name": "moraDiff", + "type": "checkbox", + "label": "运行前后识别摩拉数量", + "default": "true" } ] \ No newline at end of file