diff --git a/repo/js/ArtifactExpCount/README.md b/repo/js/ArtifactExpCount/README.md new file mode 100644 index 000000000..fe93489fa --- /dev/null +++ b/repo/js/ArtifactExpCount/README.md @@ -0,0 +1,2 @@ +统计背包中的圣遗物经验和狗粮数量,建议搭配JS通知使用。 + diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/ConfirmButton.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/ConfirmButton.png new file mode 100644 index 000000000..78d72f299 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/ConfirmButton.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/三星.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/三星.png new file mode 100644 index 000000000..3a2f51a49 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/三星.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/分解.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/分解.png new file mode 100644 index 000000000..7097dbefc Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/分解.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/分解未锁定.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/分解未锁定.png new file mode 100644 index 000000000..a6320614a Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/分解未锁定.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/四星.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/四星.png new file mode 100644 index 000000000..417ad4486 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/四星.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/快速选择.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/快速选择.png new file mode 100644 index 000000000..25f4961fd Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/快速选择.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/执行分解.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/执行分解.png new file mode 100644 index 000000000..6e436804b Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/执行分解.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/时间顺序.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/时间顺序.png new file mode 100644 index 000000000..93d1db10c Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/时间顺序.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/未满级.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/未满级.png new file mode 100644 index 000000000..457c2cbb2 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/未满级.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/未装备.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/未装备.png new file mode 100644 index 000000000..44f4bc0bc Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/未装备.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/未锁定.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/未锁定.png new file mode 100644 index 000000000..054b3b7b5 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/未锁定.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/确认.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/确认.png new file mode 100644 index 000000000..5a8dea209 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/确认.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/祝圣之霜定义.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/祝圣之霜定义.png new file mode 100644 index 000000000..1e1827bd8 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/祝圣之霜定义.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/筛选.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/筛选.png new file mode 100644 index 000000000..94ed28b16 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/筛选.png differ diff --git a/repo/js/ArtifactExpCount/assets/RecognitionObject/重置.png b/repo/js/ArtifactExpCount/assets/RecognitionObject/重置.png new file mode 100644 index 000000000..249d93863 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/RecognitionObject/重置.png differ diff --git a/repo/js/ArtifactExpCount/assets/已储存经验数字/0.png b/repo/js/ArtifactExpCount/assets/已储存经验数字/0.png new file mode 100644 index 000000000..b7f065ad9 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/已储存经验数字/0.png differ diff --git a/repo/js/ArtifactExpCount/assets/已储存经验数字/1.png b/repo/js/ArtifactExpCount/assets/已储存经验数字/1.png new file mode 100644 index 000000000..350d00f05 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/已储存经验数字/1.png differ diff --git a/repo/js/ArtifactExpCount/assets/已储存经验数字/2.png b/repo/js/ArtifactExpCount/assets/已储存经验数字/2.png new file mode 100644 index 000000000..6c6ee9440 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/已储存经验数字/2.png differ diff --git a/repo/js/ArtifactExpCount/assets/已储存经验数字/3.png b/repo/js/ArtifactExpCount/assets/已储存经验数字/3.png new file mode 100644 index 000000000..1da9cd0bf Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/已储存经验数字/3.png differ diff --git a/repo/js/ArtifactExpCount/assets/已储存经验数字/4.png b/repo/js/ArtifactExpCount/assets/已储存经验数字/4.png new file mode 100644 index 000000000..86480edae Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/已储存经验数字/4.png differ diff --git a/repo/js/ArtifactExpCount/assets/已储存经验数字/5.png b/repo/js/ArtifactExpCount/assets/已储存经验数字/5.png new file mode 100644 index 000000000..4da0de5b7 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/已储存经验数字/5.png differ diff --git a/repo/js/ArtifactExpCount/assets/已储存经验数字/6.png b/repo/js/ArtifactExpCount/assets/已储存经验数字/6.png new file mode 100644 index 000000000..37cdb5dbf Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/已储存经验数字/6.png differ diff --git a/repo/js/ArtifactExpCount/assets/已储存经验数字/7.png b/repo/js/ArtifactExpCount/assets/已储存经验数字/7.png new file mode 100644 index 000000000..e4592e848 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/已储存经验数字/7.png differ diff --git a/repo/js/ArtifactExpCount/assets/已储存经验数字/8.png b/repo/js/ArtifactExpCount/assets/已储存经验数字/8.png new file mode 100644 index 000000000..640ed54be Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/已储存经验数字/8.png differ diff --git a/repo/js/ArtifactExpCount/assets/已储存经验数字/9.png b/repo/js/ArtifactExpCount/assets/已储存经验数字/9.png new file mode 100644 index 000000000..71948a71d Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/已储存经验数字/9.png differ diff --git a/repo/js/ArtifactExpCount/assets/背包数字/0.png b/repo/js/ArtifactExpCount/assets/背包数字/0.png new file mode 100644 index 000000000..da4d07462 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/背包数字/0.png differ diff --git a/repo/js/ArtifactExpCount/assets/背包数字/1.png b/repo/js/ArtifactExpCount/assets/背包数字/1.png new file mode 100644 index 000000000..4db8e14c2 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/背包数字/1.png differ diff --git a/repo/js/ArtifactExpCount/assets/背包数字/2.png b/repo/js/ArtifactExpCount/assets/背包数字/2.png new file mode 100644 index 000000000..1b4ebe363 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/背包数字/2.png differ diff --git a/repo/js/ArtifactExpCount/assets/背包数字/3.png b/repo/js/ArtifactExpCount/assets/背包数字/3.png new file mode 100644 index 000000000..b37488276 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/背包数字/3.png differ diff --git a/repo/js/ArtifactExpCount/assets/背包数字/4.png b/repo/js/ArtifactExpCount/assets/背包数字/4.png new file mode 100644 index 000000000..0bf9fcbce Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/背包数字/4.png differ diff --git a/repo/js/ArtifactExpCount/assets/背包数字/5.png b/repo/js/ArtifactExpCount/assets/背包数字/5.png new file mode 100644 index 000000000..6b2b2c92b Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/背包数字/5.png differ diff --git a/repo/js/ArtifactExpCount/assets/背包数字/6.png b/repo/js/ArtifactExpCount/assets/背包数字/6.png new file mode 100644 index 000000000..a9d099f55 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/背包数字/6.png differ diff --git a/repo/js/ArtifactExpCount/assets/背包数字/7.png b/repo/js/ArtifactExpCount/assets/背包数字/7.png new file mode 100644 index 000000000..17770e559 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/背包数字/7.png differ diff --git a/repo/js/ArtifactExpCount/assets/背包数字/8.png b/repo/js/ArtifactExpCount/assets/背包数字/8.png new file mode 100644 index 000000000..00e507db7 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/背包数字/8.png differ diff --git a/repo/js/ArtifactExpCount/assets/背包数字/9.png b/repo/js/ArtifactExpCount/assets/背包数字/9.png new file mode 100644 index 000000000..d9fec1daa Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/背包数字/9.png differ diff --git a/repo/js/ArtifactExpCount/assets/选中狗粮数字/0.png b/repo/js/ArtifactExpCount/assets/选中狗粮数字/0.png new file mode 100644 index 000000000..2282d944a Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/选中狗粮数字/0.png differ diff --git a/repo/js/ArtifactExpCount/assets/选中狗粮数字/1.png b/repo/js/ArtifactExpCount/assets/选中狗粮数字/1.png new file mode 100644 index 000000000..68849403c Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/选中狗粮数字/1.png differ diff --git a/repo/js/ArtifactExpCount/assets/选中狗粮数字/2.png b/repo/js/ArtifactExpCount/assets/选中狗粮数字/2.png new file mode 100644 index 000000000..70f0ea7e2 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/选中狗粮数字/2.png differ diff --git a/repo/js/ArtifactExpCount/assets/选中狗粮数字/3.png b/repo/js/ArtifactExpCount/assets/选中狗粮数字/3.png new file mode 100644 index 000000000..706103447 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/选中狗粮数字/3.png differ diff --git a/repo/js/ArtifactExpCount/assets/选中狗粮数字/4.png b/repo/js/ArtifactExpCount/assets/选中狗粮数字/4.png new file mode 100644 index 000000000..b541b92f2 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/选中狗粮数字/4.png differ diff --git a/repo/js/ArtifactExpCount/assets/选中狗粮数字/5.png b/repo/js/ArtifactExpCount/assets/选中狗粮数字/5.png new file mode 100644 index 000000000..f8130b0cc Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/选中狗粮数字/5.png differ diff --git a/repo/js/ArtifactExpCount/assets/选中狗粮数字/6.png b/repo/js/ArtifactExpCount/assets/选中狗粮数字/6.png new file mode 100644 index 000000000..2690e5ab4 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/选中狗粮数字/6.png differ diff --git a/repo/js/ArtifactExpCount/assets/选中狗粮数字/7.png b/repo/js/ArtifactExpCount/assets/选中狗粮数字/7.png new file mode 100644 index 000000000..4d57b82fc Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/选中狗粮数字/7.png differ diff --git a/repo/js/ArtifactExpCount/assets/选中狗粮数字/8.png b/repo/js/ArtifactExpCount/assets/选中狗粮数字/8.png new file mode 100644 index 000000000..d58943086 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/选中狗粮数字/8.png differ diff --git a/repo/js/ArtifactExpCount/assets/选中狗粮数字/9.png b/repo/js/ArtifactExpCount/assets/选中狗粮数字/9.png new file mode 100644 index 000000000..627f34d23 Binary files /dev/null and b/repo/js/ArtifactExpCount/assets/选中狗粮数字/9.png differ diff --git a/repo/js/ArtifactExpCount/main.js b/repo/js/ArtifactExpCount/main.js new file mode 100644 index 000000000..587a3e28e --- /dev/null +++ b/repo/js/ArtifactExpCount/main.js @@ -0,0 +1,610 @@ +let notify = settings.notify +let account = settings.account || "默认账户"; +(async function () { + // 设置分辨率和缩放 + setGameMetrics(1920, 1080, 1); + await genshin.returnMainUi(); + keyPress("B");//打开背包 + await sleep(1000); + // 关闭弹窗 + await close_expired_stuff_popup_window(); + let enterAttempts = 0; + while (enterAttempts < 10) { + await click(642,36); + const clicked = await clickPNG("分解",false); + if (clicked) break; // 找到并点击成功就退出循环 + await sleep(750); + enterAttempts++; + await genshin.returnMainUi(); + await sleep(100); + keyPress("B"); + await sleep(1000); + } + await clickPNG("时间顺序",true,1); + await sleep(200); + await clickPNG("筛选"); + await sleep(200); + click(30, 30); + await sleep(100); + await clickPNG("重置"); + await sleep(200); + await clickPNG("祝圣之霜定义"); + await sleep(200); + await clickPNG("未装备"); + await sleep(200); + await clickPNG("未锁定"); + await sleep(200); + await clickPNG("确认"); + await sleep(200); + click(30, 30); + await sleep(100); + const smallBottle = await getBottleCount('背包小瓶', 'assets/RecognitionObject/三星.png'); + const bigBottle = await getBottleCount('背包大瓶', 'assets/RecognitionObject/四星.png'); + await clickPNG("筛选"); + await sleep(200); + click(30, 30); + await sleep(100); + await clickPNG("重置"); + await sleep(200); + await clickPNG("确认"); + click(30, 30); + await sleep(100); + //点击分解 + await clickPNG("分解"); + await sleep(1000); + // 识别已储存经验(1570-880-1650-930) + const digits = await numberTemplateMatch("assets/已储存经验数字", 1573, 885, 74, 36); + let initialValue = 0; + if (digits >= 0) { + initialValue = digits; + log.info(`已储存经验识别成功: ${initialValue}`); + } else { + log.warn(`已储存经验值识别失败,使用默认值0`); + } + await clickPNG("快速选择"); + await sleep(500); + // 识别不同星级狗粮数量 + const starPositions = [ + { star: 1, y: 130 }, + { star: 2, y: 200 }, + { star: 3, y: 270 }, + { star: 4, y: 340 } + ]; + const starCounts = {}; + for (const { star, y } of starPositions) { + const count = await numberTemplateMatch("assets/选中狗粮数字", 570, y, 60, 50); + if (count < 0) { + log.warn(`在${star}星狗粮位置未识别到有效数字`); + starCounts[`star${star}`] = 0; // 设置默认值为0 + }else{ + starCounts[`star${star}`] = count; + log.info(`${star}星狗粮识别到${count}个`); + } + } + // 计算狗粮经验值 + const expStar1 = starCounts.star1 * 420; + const expStar2 = starCounts.star2 * 840; + const expStar3 = starCounts.star3 * 1260; + const expStar4 = starCounts.star4 * 2520; + const expStars = expStar1 + expStar2 + expStar3 + expStar4; + // 库存经验值 + const expSmall = parseInt(smallBottle || 0) * 2500; + const expBig = parseInt(bigBottle || 0) * 10000; + const expStock = expSmall + expBig + initialValue; + // 合计 + const totalExp = expStars + expStock; + const totalCount = starCounts.star1 + starCounts.star2 + starCounts.star3 + starCounts.star4; + + // 预计算所有需要格式化的值 + const formattedExpStar1 = await formatExp(expStar1); + const formattedExpStar2 = await formatExp(expStar2); + const formattedExpStar3 = await formatExp(expStar3); + const formattedExpStar4 = await formatExp(expStar4); + const formattedExpStars = await formatExp(expStars); + const formattedExpSmall = await formatExp(expSmall); + const formattedExpBig = await formatExp(expBig); + const formattedExpStock = await formatExp(expStock); + const formattedTotalExp = await formatExp(totalExp); + + // 构建通知消息 + let message = `📦 圣遗物经验统计\n`; + message += `\n`; + message += `💾 已储存经验:${initialValue} 点\n`; + message += `\n`; + message += `📊 [狗粮数量统计]\n`; + message += `📦 总数量:${totalCount} 个\n`; + message += `⭐ 1星:${starCounts.star1} 个${expStar1 > 0 ? `(${formattedExpStar1})` : ''}\n`; + message += `⭐ 2星:${starCounts.star2} 个${expStar2 > 0 ? `(${formattedExpStar2})` : ''}\n`; + message += `⭐ 3星:${starCounts.star3} 个${expStar3 > 0 ? `(${formattedExpStar3})` : ''}\n`; + message += `⭐ 4星:${starCounts.star4} 个${expStar4 > 0 ? `(${formattedExpStar4})` : ''}\n`; + message += `💰 狗粮经验合计:${formattedExpStars}\n`; + message += `\n`; + message += `🧪 [经验瓶数量]\n`; + message += `🧪 小经验瓶:${smallBottle || 0} 个${expSmall > 0 ? `(${formattedExpSmall})` : ''}\n`; + message += `🧪 大经验瓶:${bigBottle || 0} 个${expBig > 0 ? `(${formattedExpBig})` : ''}\n`; + message += `💰 库存经验合计:${formattedExpStock}(含储存${initialValue}点)\n`; + message += `\n`; + message += `✨ 总经验:${formattedTotalExp}\n`; + // 记录保存功能 + const userName = await getUserName(); + const recordPath = `assets/${userName}.txt`; + + // 获取本地保存的数据 + const localData = await getLocalData(recordPath); + + // 更新记录 + await updateRecord(recordPath, initialValue, parseInt(smallBottle || 0), parseInt(bigBottle || 0), starCounts, totalExp); + + // 构建基础日志信息 + const formattedExpStarsLog = await formatExp(expStars); + const formattedExpSmallLog = await formatExp(expSmall); + const formattedExpBigLog = await formatExp(expBig); + const formattedExpStockLog = await formatExp(expStock); + const formattedTotalExpLog = await formatExp(totalExp); + const baseLog = `狗粮:${totalCount}个(1★${starCounts.star1} 2★${starCounts.star2} 3★${starCounts.star3} 4★${starCounts.star4}) | 狗粮经验:${formattedExpStarsLog} | 小瓶:${formattedExpSmallLog} 大瓶:${formattedExpBigLog} 储存:${initialValue} | 库存经验:${formattedExpStockLog} | 总计:${formattedTotalExpLog}`; + + // 计算变化量并添加到通知消息 + let logMessage = baseLog; + if (localData.initialized.initialValue && localData.initialized.smallBottle && localData.initialized.bigBottle && + localData.initialized.star1 && localData.initialized.star2 && localData.initialized.star3 && localData.initialized.star4 && + localData.initialized.totalExp) { + // 计算本地记录中的狗粮经验合计 + const localExpStar1 = localData.starCounts.star1 * 420; + const localExpStar2 = localData.starCounts.star2 * 840; + const localExpStar3 = localData.starCounts.star3 * 1260; + const localExpStar4 = localData.starCounts.star4 * 2520; + const localExpStars = localExpStar1 + localExpStar2 + localExpStar3 + localExpStar4; + + // 计算总经验和狗粮经验合计的变化 + const diffTotalExp = totalExp - localData.totalExp; + const diffExpStars = expStars - localExpStars; + + // 添加变化量到通知消息 + message += `\n`; + if (diffTotalExp !== 0 || diffExpStars !== 0) { + if (diffTotalExp !== 0) { + const totalChangeDesc = diffTotalExp > 0 ? '增加' : '减少'; + const formattedDiffTotalExp = await formatExp(Math.abs(diffTotalExp)); + message += `📈 总经验${totalChangeDesc}:${formattedDiffTotalExp}\n`; + } + if (diffExpStars !== 0) { + const expStarsChangeDesc = diffExpStars > 0 ? '增加' : '减少'; + const formattedDiffExpStars = await formatExp(Math.abs(diffExpStars)); + message += `🐶 未分解的狗粮经验${expStarsChangeDesc}:${formattedDiffExpStars}`; + } + } else { + // 如果没有变化,输出不变 + message += `📊 经验数据无变化`; + } + + // 构建完整日志(包含变化信息) + if (diffTotalExp !== 0 || diffExpStars !== 0) { + const totalChangeDesc = diffTotalExp > 0 ? '增加' : '减少'; + const expStarsChangeDesc = diffExpStars > 0 ? '增加' : '减少'; + const formattedDiffTotalExpLog = await formatExp(Math.abs(diffTotalExp)); + const formattedDiffExpStarsLog = await formatExp(Math.abs(diffExpStars)); + logMessage = `总经验${totalChangeDesc}:${formattedDiffTotalExpLog} | 狗粮经验${expStarsChangeDesc}:${formattedDiffExpStarsLog} | ${baseLog}`; + } else { + // 如果没有变化,日志也显示不变 + logMessage = `经验数据无变化 | ${baseLog}`; + } + } + if (settings.notify) { + notification.send(message); + } + log.info(logMessage); + await genshin.returnMainUi(); + + // 格式化经验值显示 + async function formatExp(num) { + if (num >= 10000) { + // 直接除法并转换为字符串,保留所有有效小数 + return `${(num / 10000).toString()}万`; + } else { + return `${num}`; + } + } + /** + * 在指定区域内,用 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.8, + splitCount = 3, + 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 clickPNG(png, doClick = true, maxAttempts = 40, Threshold = 0.9) { + const pngRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`assets/RecognitionObject/${png}.png`)); + pngRo.Threshold = Threshold; + pngRo.InitTemplate(); + return await findAndClick(pngRo, maxAttempts, doClick); + } + + async function findAndClick(target, maxAttempts = 20, doClick) { + //log.info("调试-开始检查"); + for (let i = 0; i < maxAttempts; i++) { + //log.info("调试-检查一次"); + const rg = captureGameRegion(); + try { + const res = rg.find(target); + if (res.isExist()) { if (doClick) await sleep(16), res.click(), await sleep(50); return true; } + } finally { rg.dispose(); } + if (i < maxAttempts - 1) await sleep(50); + } + return false; + } + + // 检验账户名 + async function getUserName() { + userName = userName.trim(); + // 账户名规则:数字、中英文,长度1-20字符 + if (!userName || !/^[\u4e00-\u9fa5A-Za-z0-9]{1,20}$/.test(userName)) { + log.error(`账户名${userName}违规,暂时使用默认账户名,请查看readme后修改`) + userName = "默认账户"; + } + return userName; + } + + async function close_expired_stuff_popup_window() { + const game_region = captureGameRegion(); + const text_x = 850; + const text_y = 273; + const text_w = 225; + const text_h = 51; + const ocr_res = game_region.find(RecognitionObject.ocr(text_x, text_y, text_w, text_h)); + if (ocr_res) { + if (ocr_res.text.includes("物品过期")) { + log.info("检测到物品过期"); + click(1000, 750); + await sleep(1000); + } + } + game_region.dispose(); + } + /** + * 识别背包中指定物品的数量 + * @param {string} itemName - 物品名称(仅用于日志) + * @param {string} templatePath - 模板图片路径 + * @returns {Promise} 识别到的数字字符串(可能为空) + */ + async function getBottleCount(itemName, templatePath) { + const ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync(templatePath)); + ro.InitTemplate(); + for (let i = 0; i < 5; i++) { + const rg = captureGameRegion(); + try { + const res = rg.find(ro); + if (res.isExist()) { + const regionToCheck = { x: res.x, y: res.y + 20, width: 70, height: 20 }; + // 使用numberTemplateMatch函数识别数字 + const count = await numberTemplateMatch( + 'assets/背包数字', // 数字模板文件夹路径 + regionToCheck.x, regionToCheck.y, regionToCheck.width, regionToCheck.height + ); + const digits = count === -1 ? '' : count.toString(); + log.info(`识别到${itemName}数量为${digits}`); + //log.info(`识别到${itemName}识别区域为${regionToCheck.x}, ${regionToCheck.y}, ${regionToCheck.width}, ${regionToCheck.height}`) + return digits; // 成功识别即返回 + } + } finally { + rg.dispose(); + } + if (i < 5 - 1) await sleep(50); + } + return ''; // 未找到时返回空字符串 + } + + // 检验账户名 + async function getUserName() { + account = account.trim(); + // 账户名规则:数字、中英文,长度1-20字符 + if (!account || !/^[\u4e00-\u9fa5A-Za-z0-9]{1,20}$/.test(account)) { + log.error(`账户名${account}违规,暂时使用默认账户名,请查看readme后修改`) + account = "默认账户"; + } + return account; + } + + /** + * 获取本地记录中最新的一组数据 + * @param {string} filePath - 记录文件路径 + * @returns {Promise} 包含经验数据的对象 + */ + async function getLocalData(filePath) { + // 初始化返回结果 + const result = { + initialValue: null, + smallBottle: null, + bigBottle: null, + starCounts: { + star1: null, + star2: null, + star3: null, + star4: null + }, + totalExp: null, + initialized: { + initialValue: false, + smallBottle: false, + bigBottle: false, + star1: false, + star2: false, + star3: false, + star4: false, + totalExp: false + } + }; + + try { + // 尝试读取文件,不存在则直接返回空结果 + const content = await file.readText(filePath); + const lines = content.split('\n').filter(line => line.trim()); + + if (lines.length === 0) return result; + + // 数据匹配正则 + const initialValueRegex = /已储存经验-(\d+)/; + const smallBottleRegex = /小经验瓶-(\d+)/; + const bigBottleRegex = /大经验瓶-(\d+)/; + const star1Regex = /1星狗粮-(\d+)/; + const star2Regex = /2星狗粮-(\d+)/; + const star3Regex = /3星狗粮-(\d+)/; + const star4Regex = /4星狗粮-(\d+)/; + const totalExpRegex = /总经验-(\d+)/; + + // 遍历前几条记录,寻找最新的一组完整数据 + for (const line of lines) { + // 匹配已储存经验 + if (!result.initialized.initialValue) { + const match = line.match(initialValueRegex); + if (match) { + result.initialValue = parseInt(match[1]); + result.initialized.initialValue = true; + } + } + + // 匹配小经验瓶 + if (!result.initialized.smallBottle) { + const match = line.match(smallBottleRegex); + if (match) { + result.smallBottle = parseInt(match[1]); + result.initialized.smallBottle = true; + } + } + + // 匹配大经验瓶 + if (!result.initialized.bigBottle) { + const match = line.match(bigBottleRegex); + if (match) { + result.bigBottle = parseInt(match[1]); + result.initialized.bigBottle = true; + } + } + + // 匹配1星狗粮 + if (!result.initialized.star1) { + const match = line.match(star1Regex); + if (match) { + result.starCounts.star1 = parseInt(match[1]); + result.initialized.star1 = true; + } + } + + // 匹配2星狗粮 + if (!result.initialized.star2) { + const match = line.match(star2Regex); + if (match) { + result.starCounts.star2 = parseInt(match[1]); + result.initialized.star2 = true; + } + } + + // 匹配3星狗粮 + if (!result.initialized.star3) { + const match = line.match(star3Regex); + if (match) { + result.starCounts.star3 = parseInt(match[1]); + result.initialized.star3 = true; + } + } + + // 匹配4星狗粮 + if (!result.initialized.star4) { + const match = line.match(star4Regex); + if (match) { + result.starCounts.star4 = parseInt(match[1]); + result.initialized.star4 = true; + } + } + + // 匹配总经验 + if (!result.initialized.totalExp) { + const match = line.match(totalExpRegex); + if (match) { + result.totalExp = parseInt(match[1]); + result.initialized.totalExp = true; + } + } + + // 所有数据都找到,提前终止遍历 + if (result.initialized.initialValue && + result.initialized.smallBottle && + result.initialized.bigBottle && + result.initialized.star1 && + result.initialized.star2 && + result.initialized.star3 && + result.initialized.star4 && + result.initialized.totalExp) { + break; + } + } + return result; + } catch (error) { + // 文件不存在或读取错误时返回空结果 + return result; + } + } + + /** + * 更新记录文件 + * @param {string} filePath - 记录文件路径 + * @param {number} initialValue - 已储存经验 + * @param {number} smallBottle - 小经验瓶数量 + * @param {number} bigBottle - 大经验瓶数量 + * @param {object} starCounts - 狗粮数量 + * @param {number} totalExp - 总经验值 + */ + async function updateRecord(filePath, initialValue, smallBottle, bigBottle, starCounts, totalExp) { + // 生成当前时间字符串 + const now = new Date(); + const timeStr = `${now.getFullYear()}/${ + String(now.getMonth() + 1).padStart(2, '0') + }/${ + String(now.getDate()).padStart(2, '0') + } ${ + String(now.getHours()).padStart(2, '0') + }:${ + String(now.getMinutes()).padStart(2, '0') + }:${ + String(now.getSeconds()).padStart(2, '0') + }`; + + // 生成记录 + const records = [ + `时间:${timeStr}-已储存经验-${initialValue}`, + `时间:${timeStr}-小经验瓶-${smallBottle}`, + `时间:${timeStr}-大经验瓶-${bigBottle}`, + `时间:${timeStr}-1星狗粮-${starCounts.star1}`, + `时间:${timeStr}-2星狗粮-${starCounts.star2}`, + `时间:${timeStr}-3星狗粮-${starCounts.star3}`, + `时间:${timeStr}-4星狗粮-${starCounts.star4}`, + `时间:${timeStr}-总经验-${totalExp}` + ]; + + try { + let content = await file.readText(filePath); + let lines = content.split('\n').filter(line => line.trim()); + + if (lines.length === 0) { + // 文件为空,直接写入新记录 + await file.writeText(filePath, records.join('\n')); + return true; + } + + + + // 添加新记录到最前面 + lines.unshift(...records); + + // 只保留1年内的记录 + const oneYearAgo = new Date(); + oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1); + + const recentLines = lines.filter(line => { + const timeMatch = line.match(/时间:(\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2})/); + if (!timeMatch) return false; + const lineTime = new Date(timeMatch[1]); + return lineTime >= oneYearAgo; + }); + + // 写入文件 + await file.writeText(filePath, recentLines.join('\n')); + return true; + } catch (error) { + // 文件不存在时创建新文件 + await file.writeText(filePath, records.join('\n')); + return true; + } + } +})(); \ No newline at end of file diff --git a/repo/js/ArtifactExpCount/manifest.json b/repo/js/ArtifactExpCount/manifest.json new file mode 100644 index 000000000..99ed86c62 --- /dev/null +++ b/repo/js/ArtifactExpCount/manifest.json @@ -0,0 +1,18 @@ +{ + "manifest_version": 1, + "name": "圣遗物经验统计", + "version": "1.0", + "bgi_version": "0.55", + "description": "识别可分解的1-4星圣遗物和经验瓶数量", + "authors": [ + { + "name": "勺子", + "links": "https://github.com/skyflag2022" + } + ], + "settings_ui": "settings.json", + "main": "main.js", + "saved_files": [ + "assets/*.txt" + ] +} \ No newline at end of file diff --git a/repo/js/ArtifactExpCount/settings.json b/repo/js/ArtifactExpCount/settings.json new file mode 100644 index 000000000..590b44caf --- /dev/null +++ b/repo/js/ArtifactExpCount/settings.json @@ -0,0 +1,14 @@ +[ + { + "name": "userName", + "type": "input-text", + "label": "账户名称\n用于多账户运行时区分不同账户", + "default": "默认账户" + }, + { + "name": "notify", + "type": "checkbox", + "label": "是否通知信息", + "default": true + } +] \ No newline at end of file