JS脚本:AAA充能批发 (#2806)

* 原石购买粉球

* 博士周本充能
This commit is contained in:
h
2026-01-28 22:18:18 +08:00
committed by GitHub
parent 552a191615
commit 114ffafd60
15 changed files with 479 additions and 0 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

View File

@@ -0,0 +1,12 @@
## 一、功能概述
### 1. 充能
- 在神像切换队伍后,前往博士周本直接把角色大招回满
## 二、版本与环境
- BGI ≥ v0.55
1.0
切换队伍充能

View 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();
}
})();

View 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"
}

View File

@@ -0,0 +1,7 @@
[
{
"name": "partyName",
"type": "input-text",
"label": "需要充能的队伍名称"
}
]

View File

@@ -0,0 +1,2 @@
适用于每天换粉球的用户,一个一个换。
ps正常运行不会换错意外换错了别抽可以找客服换回来。

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View 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(); // 调用兑换函数
})();

View 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"
}