diff --git "a/NIKKE/NIKKE\346\227\245\345\270\270.js" "b/NIKKE/NIKKE\346\227\245\345\270\270.js" index bf8a062..087a12f 100644 --- "a/NIKKE/NIKKE\346\227\245\345\270\270.js" +++ "b/NIKKE/NIKKE\346\227\245\345\270\270.js" @@ -1,11 +1,13 @@ var { 启动NIKKE, 等待NIKKE加载, 退出NIKKE, - mostSimilar, 返回首页, 关闭限时礼包 + mostSimilar, 返回首页, 关闭限时礼包, + detectNikkes } = require('./NIKKEutils.js'); var { 模拟室 } = require('./模拟室.js'); var { ocrUntilFound, clickRect, findImageByFeature, - requestScreenCaptureAuto, getDisplaySize + requestScreenCaptureAuto, getDisplaySize, + findContoursRect } = require('./utils.js'); let width, height; let NIKKEstorage = storages.create("NIKKEconfig"); @@ -40,7 +42,8 @@ function 日常() { 竞技场: 竞技场, 爬塔: 爬塔, 咨询: 咨询, - 模拟室: () => 模拟室(true) + 模拟室: () => 模拟室(true), + 每日任务: 每日任务 }; let alreadyRetry = 0; const maxRetry = NIKKEstorage.get('maxRetry', 1); @@ -97,7 +100,7 @@ function 商店() { clickRect(ocrUntilFound(res => res.find(e => e.text == '购买'), 30, 1000)); let affordable = true; ocrUntilFound(res => { - if (res.text.includes('不足')) { + if (res.find(e => e.text.match(/不足.?$/) != null)) { affordable = false; return true; } @@ -786,4 +789,136 @@ function 单次咨询(advise) { } toast('回到咨询首页'); return true; -} \ No newline at end of file +} + +function listEquip() { + let [equip, leftBound, lowerBound] = ocrUntilFound(res => { + let e = res.find(e => e.text == 'EQUIP'); + let le = res.find(e => e.text.match(/(装备|技能|魔方)/) != null); + let lo = res.find(e => e.text.match(/(快捷|全部)/) != null); + if (!e || !le || !lo) + return null; + return [e.bounds.bottom, le.bounds.right, lo.bounds.top]; + }, 30, 300); + let ret = []; + let equipHeight = lowerBound - equip; + for (let i = 0; i < 10; ++i) { + ret = findContoursRect(captureScreen(), { + thresh: 160, + region: [leftBound, equip, width - leftBound, equipHeight] + }).filter(rect => { + if (rect.height() < equipHeight / 4 || rect.height() > equipHeight / 2) + return false; + if (rect.width() < equipHeight / 4 || rect.width() > equipHeight / 2) + return false; + return true; + }); + if (ret.length == 4) + break; + } + return ret; +} + +function 强化装备() { + let dailyMission = NIKKEstorage.get('dailyMission', {}); + let targetEquip = dailyMission.equipEnhanceSlot || 0; + let targetNikke = dailyMission.equipEnhanceNikke || ''; + if (targetNikke == '') { + toastLog('未指定强化装备妮姬'); + return; + } + let targetNikkeReg = new RegExp(targetNikke); + let target = null; + clickRect(ocrUntilFound(res => res.find(e => e.text == '妮姬'), 40, 1000)); + let upperBound = ocrUntilFound(res => { + let upper = res.find(e => e.text == 'ALL'); + if (!upper) + return null; + return upper.bounds.bottom; + }, 30, 600); + // 找到指定妮姬 + for (let retry = 0; target == null && retry < 3; ++retry) { + if (retry > 0) + for (let i = 0; i < 7; ++i) + swipe(width / 2, (upperBound + height) / 2, width / 2, height, 300); + sleep(1000); + let lastNikke = null; + for (let page = 0; page < 10; ++page) { + let nikkes = detectNikkes(captureScreen(), [0, upperBound]); + if (nikkes[nikkes.length - 1].name == lastNikke) + break; + lastNikke = nikkes[nikkes.length - 1].name; + let bottomY = nikkes[nikkes.length - 1].bounds.bottom; + let t = mostSimilar(targetNikke, nikkes.map(x => x.name)); + if (t.similarity > 0.5) { + target = nikkes.find(e => e.name == t.result); + break; + } + t = nikkes.find(x => targetNikkeReg.test(x.name)); + if (t != null) { + target = t; + break; + } + swipe(width / 2, bottomY, width / 2, upperBound, 1000); + swipe(100, bottomY, width / 2, bottomY, 500); + sleep(500); + } + } + if (target == null) { + console.error(`没有找到名为“${targetNikke}”的妮姬`); + clickRect(ocrUntilFound(res => res.find(e => e.text == '大厅'), 30, 1000)); + return; + } + clickRect(target, 0.01); + ocrUntilFound(res => res.text.match(/(STATUS|体力|攻击|返回)/), 30, 1000); + // 点击指定装备 + clickRect({ bounds: listEquip()[targetEquip] }); + ocrUntilFound(res => res.find(e => e.text.match(/^(升级|穿戴|交换|改造)/) == null), 20, 1000); + // 检查是否可以升级 + let enhanceBtn = ocrUntilFound(res => res.find(e => e.text.match(/^升级$/) != null), 3, 1000); + if (enhanceBtn == null) { + toastLog('指定装备不可升级'); + back(); + 返回首页(); + return; + } + // 检查升级材料 + clickRect(enhanceBtn); + let [enhanceConfirm, equipUpperBound] = ocrUntilFound(res => { + if (!res.text.includes('自动')) + return null; + let confirm = res.find(e => e.text == '升级'); + let upper = res.find(e => e.text.match(/(择升|级别|等级)/) != null); + if (!confirm || !upper) + return null; + return [confirm, upper.bounds.bottom]; + }, 30, 1000); + let enhanceStuff = findContoursRect(captureScreen(), { + thresh: 170, + region: [0, equipUpperBound, width, enhanceConfirm.bounds.top - equipUpperBound] + }).filter(x => { + if (x.width() < 100) + return false; + if (Math.abs(x.width() - x.height()) > 20) + return false; + return true; + }); + if (enhanceStuff.length == 0) { + toastLog('没有强化材料'); + } else { + clickRect({ bounds: enhanceStuff[0] }); + clickRect(enhanceConfirm); + sleep(1000); + while (colors.blue(captureScreen().pixel( + enhanceConfirm.bounds.left, + enhanceConfirm.bounds.top + )) > 220) + sleep(300); + } + back(); + 返回首页(); +} + +function 每日任务() { + 强化装备(); +} diff --git "a/NIKKE/NIKKE\350\256\276\347\275\256.js" "b/NIKKE/NIKKE\350\256\276\347\275\256.js" index 770a9f4..b216521 100644 --- "a/NIKKE/NIKKE\350\256\276\347\275\256.js" +++ "b/NIKKE/NIKKE\350\256\276\347\275\256.js" @@ -114,78 +114,64 @@ ui.layout( 刷buff时只考虑以下增益效果: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 每日任务设置 + + 强化装备指定妮姬: + + + + 强化装备部位:头 + + + 其他设置 @@ -255,7 +241,7 @@ function checkUpdate() { const NIKKEstorage = storages.create("NIKKEconfig"); const todoTaskDefault = [ "商店", "基地收菜", "好友", "竞技场", - "爬塔", "咨询", "模拟室" + "爬塔", "咨询", "模拟室", "每日任务" ]; const simulationRoomDefault = { maxPass: 20, @@ -283,6 +269,9 @@ ui.findView('竞技场').on('check', function (checked) { ui.findView('模拟室').on('check', function (checked) { ui.simulationRoom.attr('visibility', checked ? 'visible' : 'gone'); }); +ui.findView('每日任务').on('check', function (checked) { + ui.dailyMission.attr('visibility', checked ? 'visible' : 'gone'); +}); ui.buyCodeManual.setMin(0); ui.buyCodeManual.setMax(4); @@ -366,6 +355,17 @@ for (let task of todoTask) for (let buffName of simulationRoom.preferredBuff) ui.findView(buffName).setChecked(true); +ui.equipEnhanceSlot.setMin(0); +ui.equipEnhanceSlot.setMax(3); +ui.equipEnhanceSlot.setOnSeekBarChangeListener({ + onProgressChanged: function (seekbar, p, fromUser) { + ui.equipEnhanceSlotText.setText(`强化装备部位:${'头身手腿'[p]}`); + } +}); +let dailyMission = NIKKEstorage.get('dailyMission', {}); +ui.equipEnhanceNikke.setText(dailyMission.equipEnhanceNikke || ''); +ui.equipEnhanceSlot.setProgress(dailyMission.equipEnhanceSlot || 0); + for (let generalOption of [ 'mute', 'alreadyInGame', 'checkUpdateAuto', 'checkSale', 'exitGame', 'checkDailyLogin', 'v2rayNG' @@ -414,10 +414,14 @@ ui.save.on("click", function () { toast('模拟室编队格式有误,无法保存'); return; } + if (ui.findView('每日任务').isChecked() && ui.equipEnhanceNikke.text().trim() == '') { + toast('强化装备指定妮姬不可留空,无法保存'); + return; + } let todoTask = []; for (let task of [ - "基地收菜", "好友", "竞技场", - "商店", "爬塔", "咨询", "模拟室" + "基地收菜", "好友", "竞技场", "商店", + "爬塔", "咨询", "模拟室", "每日任务" ]) if (ui.findView(task).isChecked()) todoTask.push(task); @@ -440,6 +444,11 @@ ui.save.on("click", function () { simulationRoom.preferredBuff.push(buffName); NIKKEstorage.put('simulationRoom', JSON.stringify(simulationRoom)); + let dailyMission = {}; + dailyMission.equipEnhanceNikke = ui.equipEnhanceNikke.text().trim(); + dailyMission.equipEnhanceSlot = ui.equipEnhanceSlot.getProgress(); + NIKKEstorage.put('dailyMission', dailyMission); + for (let generalOption of [ 'mute', 'alreadyInGame', 'checkUpdateAuto', 'checkSale', 'exitGame', 'checkDailyLogin', 'v2rayNG' diff --git a/utils.js b/utils.js index 52c166a..759ba7c 100644 --- a/utils.js +++ b/utils.js @@ -13,6 +13,7 @@ else { getDisplaySize: getDisplaySize, killApp: killApp, buildRegion: buildRegion, + findContoursRect: findContoursRect, findImageByFeature: findImageByFeature }; } @@ -255,6 +256,56 @@ function findImageByFeature(trainImg, queryImg, options) { } } +function findContoursRect(img, options) { + options = options || {}; + let thresh = options.thresh || 160; + let [x, y, w, h] = buildRegion(options.region, img); + let clipImg = images.clip(img, x, y, w, h); + let grayImg = images.cvtColor(clipImg, "BGR2GRAY"); + let threImg = images.threshold(grayImg, thresh, 255, "BINARY_INV"); + let ret = []; + with (JavaImporter( + org.opencv.imgproc.Imgproc, + com.stardust.autojs.core.opencv, + org.opencv.core.Core, + org.opencv.core.Point, + org.opencv.core.MatOfPoint2f, + org.opencv.core.Scalar, + com.stardust.autojs.core.opencv.Mat + )) { + let threImgMat = threImg.getMat(); + let contours = java.lang.reflect.Array.newInstance(MatOfPoint, 0); + contours = java.util.ArrayList(java.util.Arrays.asList(contours)); + Imgproc.findContours(threImgMat, contours, Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); + for (let i = 0; i < contours.size(); ++i) { + let contour2f = MatOfPoint2f(contours.get(i).toArray()); + let epsilon = Imgproc.arcLength(contour2f, true) * 0.1; + let approxCurve = MatOfPoint2f(); + Imgproc.approxPolyDP(contour2f, approxCurve, epsilon, true); + let pts = MatOfPoint(approxCurve.toArray()); + let rect = Imgproc.boundingRect(pts); + ret.push(android.graphics.Rect( + rect.x + x, + rect.y + y, + rect.x + rect.width + x, + rect.y + rect.height + y + )); + // Imgproc.rectangle(threImgMat, Point(rect.x, rect.y), Point(rect.x + rect.width, rect.y + rect.height), Scalar(150), 3); + } + // images.save(images.matToImage(threImgMat), `./images/nikkerror/${Date.now()}.jpg`); + } + threImg.recycle(); + grayImg.recycle(); + clipImg.recycle(); + ret.sort((a, b) => { + let t = a.top - b.top; + if (Math.abs(t) < 20) + return a.left - b.left; + return t; + }); + return ret; +} + function killApp(packageName) { var name = getPackageName(packageName); if (!name) {