1
repo/js/博士周本充能/222.json
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
BIN
repo/js/博士周本充能/Assets/RecognitionObject/Leave Button.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
repo/js/博士周本充能/Assets/RecognitionObject/Quick Setup Button.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
repo/js/博士周本充能/Assets/RecognitionObject/Slider Bottom.png
Normal file
|
After Width: | Height: | Size: 307 B |
BIN
repo/js/博士周本充能/Assets/RecognitionObject/Slider Top.png
Normal file
|
After Width: | Height: | Size: 517 B |
12
repo/js/博士周本充能/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
## 一、功能概述
|
||||
|
||||
### 1. 充能
|
||||
|
||||
- 在神像切换队伍后,前往博士周本直接把角色大招回满
|
||||
|
||||
## 二、版本与环境
|
||||
|
||||
- BGI ≥ v0.55
|
||||
|
||||
1.0
|
||||
切换队伍充能
|
||||
307
repo/js/博士周本充能/main.js
Normal file
@@ -0,0 +1,307 @@
|
||||
const repeatOperationUntilTextFound = async ({
|
||||
//默认区域为单个F图标右边的文字,最多6个
|
||||
x = 1210,
|
||||
y = 515,
|
||||
width = 200,
|
||||
height = 50,
|
||||
targetText = null,
|
||||
maxSteps = 100,
|
||||
stepDuration = 200,
|
||||
waitTime = 10,
|
||||
moveKey = "w",
|
||||
ifClick = false,
|
||||
} = {}) => {
|
||||
/**
|
||||
* 转义正则表达式中的特殊字符
|
||||
* @param {string} string 要转义的字符串
|
||||
* @returns {string} 转义后的字符串
|
||||
*/
|
||||
const escapeRegExp = (string) => {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
};
|
||||
|
||||
// 预编译正则表达式(如果是字符串则转换并转义)
|
||||
const textPattern = typeof targetText === 'string'
|
||||
? new RegExp(escapeRegExp(targetText))
|
||||
: targetText;
|
||||
|
||||
let stepsTaken = 0;
|
||||
|
||||
while (stepsTaken <= maxSteps) {
|
||||
// 1. 捕获游戏区域并裁剪出检测区域
|
||||
const captureRegion = captureGameRegion();
|
||||
const textArea = captureRegion.DeriveCrop(x, y, width, height);
|
||||
|
||||
// 2. 执行OCR识别
|
||||
const ocrResult = textArea.find(RecognitionObject.ocrThis);
|
||||
captureRegion.dispose();
|
||||
textArea.dispose();
|
||||
|
||||
const hasAnyText = ocrResult.text.trim().length > 0;
|
||||
const matchesTarget = targetText === null
|
||||
? hasAnyText
|
||||
: textPattern.test(ocrResult.text);
|
||||
|
||||
if (matchesTarget) {
|
||||
log.info(`检测到${targetText === null ? '文字' : '目标文字'}: ${ocrResult.text}`);
|
||||
await sleep(1000);
|
||||
if (ifClick) click(Math.round(x + width / 2), Math.round(y + height / 2));
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. 检查步数限制
|
||||
if (stepsTaken >= maxSteps) {
|
||||
throw new Error(`检查次数超过最大限制: ${maxSteps},未查询到文字"${targetText}"`);
|
||||
}
|
||||
|
||||
// 5. 前进一小步
|
||||
if (stepDuration != 0) {
|
||||
keyDown(moveKey);
|
||||
await sleep(stepDuration);
|
||||
keyUp(moveKey);
|
||||
}
|
||||
await sleep(waitTime);
|
||||
stepsTaken++;
|
||||
}
|
||||
}
|
||||
|
||||
// Party Setup
|
||||
const QuickSetupButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/Quick Setup Button.png"), 1100, 900, 400, 180);
|
||||
const ConfigureTeamButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/Configure Team Button.png"), 0, 900, 200, 180);
|
||||
const ConfirmDeployButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/Confirm Deploy Button.png"), 0, 900, 1920, 180);
|
||||
// Slider
|
||||
const LeftSliderTopRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/Slider Top.png"), 650, 50, 100, 100);
|
||||
const LeftSliderBottomRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/Slider Bottom.png"), 650, 100, 100, 900);
|
||||
const MiddleSliderTopRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/Slider Top.png"), 1250, 50, 100, 200);
|
||||
const MiddleSliderBottomRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/Slider Bottom.png"), 1250, 100, 100, 900);
|
||||
const RightSliderTopRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/Slider Top.png"), 1750, 100, 100, 100);
|
||||
const RightSliderBottomRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/Slider Bottom.png"), 1750, 100, 100, 900);
|
||||
|
||||
(async function () {
|
||||
// 翻页
|
||||
async function pageDown(SliderBottomRo) {
|
||||
let captureRegion = captureGameRegion();
|
||||
let SliderBottom = captureRegion.find(SliderBottomRo);
|
||||
captureRegion.dispose();
|
||||
if (SliderBottom.isExist()) {
|
||||
log.info("当前页面已识别&点击完毕,向下滑动");
|
||||
// log.info("滑块当前位置:({x},{y},{h},{w})", SliderBottom.x, SliderBottom.y, SliderBottom.Width, SliderBottom.Height);
|
||||
click(Math.ceil(SliderBottom.x + SliderBottom.Width / 2), Math.ceil(SliderBottom.y + SliderBottom.Height * 2));
|
||||
await moveMouseTo(0, 0);
|
||||
await sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
// 滑条顶端
|
||||
async function pageTop(SliderTopRo) {
|
||||
let captureRegion = captureGameRegion();
|
||||
let SliderTop = captureRegion.find(SliderTopRo);
|
||||
captureRegion.dispose();
|
||||
if (SliderTop.isExist()) {
|
||||
log.info("识别到滑条顶端位置:({x},{y},{h},{w})", SliderTop.x, SliderTop.y, SliderTop.Width, SliderTop.Height);
|
||||
await moveMouseTo(Math.ceil(SliderTop.x + SliderTop.Width / 2), Math.ceil(SliderTop.y + SliderTop.Height * 1));
|
||||
leftButtonDown();
|
||||
await sleep(1000);
|
||||
leftButtonUp();
|
||||
await moveMouseTo(0, 0);
|
||||
await sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
async function goToWeeklyBossAndEnter() {
|
||||
// 确保主界面
|
||||
await genshin.returnMainUi();
|
||||
|
||||
await genshin.tp(9530.678, 6394.4453);
|
||||
|
||||
// 等待传送完成
|
||||
await sleep(1000);
|
||||
|
||||
// 靠近副本门口,直到可交互
|
||||
await repeatOperationUntilTextFound();
|
||||
|
||||
await sleep(500);
|
||||
|
||||
// 打开挑战界面
|
||||
keyPress("F");
|
||||
|
||||
await sleep(2000);
|
||||
|
||||
// 等待「单人挑战」按钮出现
|
||||
await repeatOperationUntilTextFound({
|
||||
x: 1650,
|
||||
y: 1000,
|
||||
width: 160,
|
||||
height: 45,
|
||||
targetText: "单人挑战",
|
||||
stepDuration: 0,
|
||||
waitTime: 100
|
||||
});
|
||||
|
||||
// 点击「单人挑战」
|
||||
click(1725, 1020);
|
||||
|
||||
await sleep(300);
|
||||
|
||||
// 处理可能出现的提示弹窗
|
||||
click(1180, 760);
|
||||
|
||||
// 等待并点击「开始挑战」
|
||||
await repeatOperationUntilTextFound({
|
||||
x: 1650,
|
||||
y: 1000,
|
||||
width: 160,
|
||||
height: 45,
|
||||
targetText: "开始挑战",
|
||||
stepDuration: 0,
|
||||
waitTime: 100,
|
||||
ifClick: true
|
||||
});
|
||||
log.info(`已进入周本`);
|
||||
await sleep(3000);
|
||||
log.info("开始充能");
|
||||
await keyMouseScript.runFile("222.json");
|
||||
log.info("充能完成");
|
||||
}
|
||||
|
||||
// 切换队伍
|
||||
async function SwitchParty(partyName) {
|
||||
let ConfigureStatue = false;
|
||||
|
||||
let foundQuickSetup = false;
|
||||
for (let j = 0; j < 2; j++) { // 尝试两次
|
||||
keyPress("VK_L");
|
||||
await sleep(2000);
|
||||
for (let i = 0; i < 2; i++) {
|
||||
let captureRegion = captureGameRegion();
|
||||
let QuickSetupButton = captureRegion.find(QuickSetupButtonRo);
|
||||
captureRegion.dispose();
|
||||
if (QuickSetupButton.isExist()) {
|
||||
log.info("已进入队伍配置页面");
|
||||
foundQuickSetup = true;
|
||||
break;
|
||||
} else {
|
||||
await sleep(1000);
|
||||
}
|
||||
}
|
||||
if (foundQuickSetup) {
|
||||
await goToWeeklyBossAndEnter();
|
||||
await genshin.TpToStatueOfTheSeven();
|
||||
break; // 第一次找到就退出循环
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundQuickSetup) {
|
||||
log.error("两次尝试都未能进入队伍配置页面");
|
||||
return false;
|
||||
}
|
||||
// 识别当前队伍
|
||||
let captureRegion = captureGameRegion();
|
||||
let resList = captureRegion.findMulti(RecognitionObject.ocr(100, 900, 300, 180));
|
||||
captureRegion.dispose();
|
||||
let currentPartyFound = false;
|
||||
|
||||
for (let i = 0; i < resList.count; i++) {
|
||||
let res = resList[i];
|
||||
log.info("当前队伍名称位置:({x},{y},{w},{h}), 识别结果:{text}", res.x, res.y, res.Width, res.Height, res.text);
|
||||
if (res.text.includes(partyName)) {
|
||||
log.info("当前队伍即为目标队伍,无需切换");
|
||||
notification.send(`当前队伍即为目标队伍:${partyName},无需切换`);
|
||||
keyPress("VK_ESCAPE");
|
||||
await sleep(500);
|
||||
currentPartyFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!currentPartyFound) {
|
||||
await sleep(1000);
|
||||
let captureRegion = captureGameRegion();
|
||||
let ConfigureTeamButton = captureRegion.find(ConfigureTeamButtonRo);
|
||||
captureRegion.dispose();
|
||||
if (ConfigureTeamButton.isExist()) {
|
||||
log.info("识别到配置队伍按钮");
|
||||
ConfigureTeamButton.click();
|
||||
await sleep(500);
|
||||
await pageTop(LeftSliderTopRo);
|
||||
|
||||
for (let p = 0; p < 4; p++) {
|
||||
// 识别当前页
|
||||
let captureRegion = captureGameRegion();
|
||||
let resList = captureRegion.findMulti(RecognitionObject.ocr(0, 100, 400, 900));
|
||||
captureRegion.dispose();
|
||||
for (let i = 0; i < resList.count; i++) {
|
||||
let res = resList[i];
|
||||
|
||||
if (res.text.includes(partyName)) {
|
||||
log.info("目标队伍位置:({x},{y},{w},{h}), 识别结果:{text}", res.x, res.y, res.Width, res.Height, res.text);
|
||||
click(Math.ceil(res.x + 360), res.y + Math.ceil(res.Height / 2));
|
||||
|
||||
// 找到目标队伍,点击确定、部署
|
||||
await sleep(1500);
|
||||
let ConfirmButtonCaptureRegion = captureGameRegion();
|
||||
let ConfirmButton = ConfirmButtonCaptureRegion.find(ConfirmDeployButtonRo);
|
||||
ConfirmButtonCaptureRegion.dispose();
|
||||
if (ConfirmButton.isExist()) {
|
||||
log.info("识别到确定按钮:({x},{y},{w},{h})", ConfirmButton.x, ConfirmButton.y, ConfirmButton.Width, ConfirmButton.Height);
|
||||
ConfirmButton.click();
|
||||
}
|
||||
await sleep(1500);
|
||||
let DeployButtonCaptureRegion = captureGameRegion();
|
||||
let DeployButton = DeployButtonCaptureRegion.find(ConfirmDeployButtonRo);
|
||||
DeployButtonCaptureRegion.dispose();
|
||||
if (DeployButton.isExist()) {
|
||||
log.info("识别到部署按钮:({x},{y},{w},{h})", DeployButton.x, DeployButton.y, DeployButton.Width, DeployButton.Height);
|
||||
DeployButton.click();
|
||||
await sleep(100);
|
||||
notification.send(`寻找到目标队伍:${partyName}`);
|
||||
ConfigureStatue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ConfigureStatue) {
|
||||
await genshin.returnMainUi();
|
||||
break;
|
||||
} else {
|
||||
await pageDown(LeftSliderBottomRo);
|
||||
}
|
||||
}
|
||||
if (!ConfigureStatue) {
|
||||
// 没找到指定队伍名称的队伍,抛出异常
|
||||
log.error(`没有找到指定队伍名称:${partyName}`);
|
||||
notification.error(`没有找到指定队伍名称:${partyName}`);
|
||||
await genshin.returnMainUi();
|
||||
throw new Error(`没有找到指定队伍名称:${partyName}`);
|
||||
}
|
||||
} else {
|
||||
await genshin.returnMainUi();
|
||||
}
|
||||
} else {
|
||||
// 当前队伍就是目标队伍,设置成功状态
|
||||
ConfigureStatue = true;
|
||||
}
|
||||
return ConfigureStatue;
|
||||
}
|
||||
|
||||
// Main
|
||||
if (!!settings.partyName) {
|
||||
try {
|
||||
log.info("强制传送到七天神像切换队伍");
|
||||
await genshin.TpToStatueOfTheSeven();
|
||||
|
||||
log.info("正在尝试切换至" + settings.partyName);
|
||||
await SwitchParty(settings.partyName);
|
||||
|
||||
genshin.clearPartyCache();
|
||||
|
||||
} catch (error) {
|
||||
log.error("队伍切换失败:" + error.message);
|
||||
notification.error("队伍切换失败:" + error.message);
|
||||
await genshin.returnMainUi();
|
||||
}
|
||||
} else {
|
||||
log.error("没有设置切换队伍");
|
||||
notification.error("没有设置切换队伍");
|
||||
await genshin.returnMainUi();
|
||||
}
|
||||
})();
|
||||
15
repo/js/博士周本充能/manifest.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "切换队伍并传送博士周本充能",
|
||||
"version": "1.0",
|
||||
"bgi_version": "0.55.3",
|
||||
"description": "进入博士周本给角色充能",
|
||||
"authors": [
|
||||
{
|
||||
"name": "爱丽丝",
|
||||
"links":"https://github.com/itslyh"
|
||||
}
|
||||
],
|
||||
"settings_ui": "settings.json",
|
||||
"main": "main.js"
|
||||
}
|
||||
7
repo/js/博士周本充能/settings.json
Normal file
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "partyName",
|
||||
"type": "input-text",
|
||||
"label": "需要充能的队伍名称"
|
||||
}
|
||||
]
|
||||
2
repo/js/原石购买粉球/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
适用于每天换粉球的用户,一个一个换。
|
||||
ps:正常运行不会换错,意外换错了别抽可以找客服换回来。
|
||||
BIN
repo/js/原石购买粉球/assets/pinkBall.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
121
repo/js/原石购买粉球/main.js
Normal file
@@ -0,0 +1,121 @@
|
||||
const replacementMap = {
|
||||
"监": "盐",
|
||||
"卵": "卯"
|
||||
};
|
||||
|
||||
// 定义一个独立的函数用于在指定区域进行 OCR 识别并输出识别内容
|
||||
async function recognizeTextInRegion(ocrRegion, timeout = 5000) {
|
||||
let startTime = Date.now();
|
||||
let retryCount = 0; // 重试计数
|
||||
while (Date.now() - startTime < timeout) {
|
||||
try {
|
||||
// 在指定区域进行 OCR 识别
|
||||
const ro = captureGameRegion();
|
||||
let ocrResult = ro.find(RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height));
|
||||
ro.dispose();
|
||||
if (ocrResult) {
|
||||
// 后处理:根据替换映射表检查和替换错误识别的字符
|
||||
let correctedText = ocrResult.text;
|
||||
for (let [wrongChar, correctChar] of Object.entries(replacementMap)) {
|
||||
correctedText = correctedText.replace(new RegExp(wrongChar, 'g'), correctChar);
|
||||
}
|
||||
return correctedText; // 返回识别到的内容
|
||||
} else {
|
||||
log.warn(`OCR 识别区域未找到内容`);
|
||||
await sleep(500);
|
||||
return null; // 如果 OCR 未识别到内容,返回 null
|
||||
}
|
||||
} catch (error) {
|
||||
retryCount++; // 增加重试计数
|
||||
await sleep(500);
|
||||
log.warn(`OCR 数识别失败,正在进行第 ${retryCount} 次重试...`);
|
||||
}
|
||||
await sleep(500); // 短暂延迟,避免过快循环
|
||||
}
|
||||
log.warn(`经过多次尝试,仍然无法在指定区域识别到文字`);
|
||||
return null; // 如果未识别到文字,返回 null
|
||||
}
|
||||
|
||||
// 获取并判断原石数量的函数
|
||||
async function checkAndExchangeYuanShi() {
|
||||
|
||||
// 获取原石数量的OCR区域
|
||||
let ocrRegionYuanShi = { x: 1585, y: 25, width: 180, height: 46 }; // 设置原石的OCR区域
|
||||
let recognizedText1 = await recognizeTextInRegion(ocrRegionYuanShi);
|
||||
|
||||
if (recognizedText1) {
|
||||
// 提取有效的数字(只保留数字部分)
|
||||
recognizedText1 = recognizedText1.replace(/\D/g, '');
|
||||
log.info(`成功识别到原石数值: ${recognizedText1}`);
|
||||
|
||||
// 判断原石数量是否足够兑换一个粉球(160原石)
|
||||
if (parseInt(recognizedText1, 10) >= 160) {
|
||||
log.info(`原石数量充足,执行兑换操作...`);
|
||||
return true; // 返回 true 表示原石数量足够,继续兑换
|
||||
} else {
|
||||
log.warn(`原石数量不足,跳过兑换`);
|
||||
return false; // 如果未识别到原石数量,返回 false
|
||||
}
|
||||
} else {
|
||||
log.warn(`未能识别到原石数量,跳过兑换`);
|
||||
return false; // 如果未识别到原石数量,返回 false
|
||||
}
|
||||
}
|
||||
|
||||
// 执行兑换操作的函数
|
||||
async function executeExchange() {
|
||||
let continueExchanging = true; // 用于控制是否继续兑换
|
||||
|
||||
while (continueExchanging) {
|
||||
|
||||
// 模板匹配粉球并进行兑换
|
||||
const pinkBallRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/pinkBall.png"));
|
||||
let ro1 = captureGameRegion(); // 获取游戏区域
|
||||
let pinkBall = ro1.find(pinkBallRo); // 使用模板匹配查找粉球按钮
|
||||
ro1.dispose(); // 清理资源
|
||||
|
||||
if (pinkBall.isExist()) {
|
||||
pinkBall.click(); await sleep(1000); // 点击粉球按钮
|
||||
click(1164, 782); await sleep(500); // 确认兑换
|
||||
click(960, 754); await sleep(1000); // 点击空白处继续
|
||||
|
||||
// 每次兑换后检查原石数量
|
||||
continueExchanging = await checkAndExchangeYuanShi(); // 如果原石不足,继续为 false,停止兑换
|
||||
|
||||
// 如果原石不足,不需要继续兑换
|
||||
if (!continueExchanging) {
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
log.warn("未能找到粉球按钮,跳过兑换");
|
||||
continueExchanging = false; // 如果没有找到粉球按钮,停止兑换
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function exchangeGoods() {
|
||||
|
||||
await genshin.returnMainUi();await sleep(1000);// 返回主界面
|
||||
keyPress("ESCAPE"); await sleep(2000);//呼叫派蒙
|
||||
click(198,416);await sleep(2000);//点击商城
|
||||
|
||||
// 如果原石数量充足,继续进行尘辉兑换
|
||||
click(127,434);await sleep(1000);//尘辉兑换
|
||||
let canExchange = await checkAndExchangeYuanShi(); // 获取原石数量并判断是否足够
|
||||
if (!canExchange) {
|
||||
log.warn("原石不足,跳过商城兑换");
|
||||
return; // 如果原石不足,直接退出,不执行后续操作
|
||||
}
|
||||
click(1230,120);await sleep(1000);//原石购买
|
||||
await executeExchange();
|
||||
|
||||
// 通知完成
|
||||
notification.send(`商城抽卡资源兑换完成`);
|
||||
}
|
||||
|
||||
|
||||
|
||||
(async function () {
|
||||
await exchangeGoods(); // 调用兑换函数
|
||||
})();
|
||||
14
repo/js/原石购买粉球/manifest.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "原石购买粉球",
|
||||
"version": "1.0",
|
||||
"description": "运行即可实现原石换粉球",
|
||||
"authors": [
|
||||
{
|
||||
"name": "爱丽丝",
|
||||
"links": "https://github.com/itslyh"
|
||||
}
|
||||
],
|
||||
"settings_ui": "settings.json",
|
||||
"main": "main.js"
|
||||
}
|
||||