mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-19 03:59:51 +08:00
feat: 千星刷经验重构 (#2897)
This commit is contained in:
@@ -8,24 +8,31 @@
|
||||
|
||||
## ❗ 使用前重要说明(免责声明)
|
||||
|
||||
本脚本依赖“**删除第一个奇域地图存档**”来实现重复获取成就经验。
|
||||
本脚本依赖“**删除奇域地图存档**”来实现重复获取成就经验。
|
||||
|
||||
这意味着:
|
||||
|
||||
- 当脚本执行删除存档时,如果 **第一个存档不是你正在刷的地图**,可能会误删你正常游玩的关卡数据。
|
||||
- 当脚本执行删除存档时,可能会误删你正常游玩的关卡数据。
|
||||
|
||||
若你完全理解并接受此风险,请在脚本的 *自定义 JS 配置* 中手动勾选 `我已阅读说明中的免责声明` 才可继续执行脚本。
|
||||
请务必确认你能承担潜在损失后再继续使用。
|
||||
|
||||
---
|
||||
|
||||
## 🔥 焚诀
|
||||
|
||||
自己发布的千星奇域的地图也可以用来增长经验,但是并不需要发布成功,仅需要点击一下发布再预览详情,即可在弹窗中进行收藏。
|
||||
这就意味着可以自行制作秒刷图,然后“发布”进行收藏,在收藏中使用自己实际上并未真正发布的地图进行秒刷。
|
||||
最新版脚本进行了重构,优化了点击速度,秒刷图一周刷完仅需8分45秒。
|
||||
地图制作请自行学习。
|
||||
|
||||
## 🌟 功能特点
|
||||
|
||||
- 🔁 自动重复通关指定奇域地图
|
||||
- 🧹 自动删除存档,实现成就经验可重复获取
|
||||
- 📅 自动监测每周经验上限,到达后自动停止
|
||||
- 🏞️ 完成后自动返回提瓦特大陆,不影响其他自动化脚本
|
||||
- ⭐ 可刷收藏地图,支持自定义地图名称
|
||||
* 自动重复通关指定奇域地图
|
||||
* 自动删除存档,实现成就经验可重复获取
|
||||
* 自动监测每周经验上限,到达后自动停止
|
||||
* 完成后自动返回提瓦特大陆,不影响其他自动化脚本
|
||||
* 可刷收藏地图,支持自定义地图名称
|
||||
|
||||
---
|
||||
|
||||
@@ -47,9 +54,10 @@
|
||||
## ❗ 注意事项
|
||||
|
||||
- 游戏窗口需保持 **16:9** 的宽高比例,否则可能影响图像识别。
|
||||
- `singleExp` 请填写正确,否则脚本的剩余次数计算会不准确。
|
||||
- `每次通关可获得的经验值` 请填写正确,否则脚本的剩余次数计算会不准确。
|
||||
- 若默认地图下架,请手动输入可游玩的地图号。
|
||||
- 勾选收藏模式后,请确保 `starRoomName` 填写正确地图名称。
|
||||
- 勾选收藏模式后,请确保 `收藏模式所使用的地图名称` 填写正确地图名称。
|
||||
- 成就模式和收藏模式可以同时开启,但请确保地图符合对应要求。
|
||||
- 若出现红色报错,提示文件不存在,无需在意,不会造成问题。
|
||||
|
||||
---
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"weekMaxExp": 4000,
|
||||
"singleExp": 270,
|
||||
"weekTotal": 15
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
const attempts = 20; // 最大尝试次数
|
||||
const interval = 1000; // 每次间隔 ms
|
||||
import {
|
||||
getImgMat,
|
||||
findText,
|
||||
findTextAndClick,
|
||||
findImgAndClick,
|
||||
waitUntilTextAppear
|
||||
} from "../../../packages/utils/tool";
|
||||
|
||||
const duration = 1000; // 默认点击等待延时
|
||||
|
||||
const storePath = "data/store.json"
|
||||
@@ -14,11 +20,6 @@ const weekMaxExp = Number(settings.weekMaxExp || "4000");
|
||||
const singleExp = Number(settings.singleExp || "270");
|
||||
let weekTotal = initWeekTotal();
|
||||
|
||||
// 获取图片资源
|
||||
function getImgMat(path) {
|
||||
return file.readImageMatSync('assets/' + path + '.png');
|
||||
}
|
||||
|
||||
// 读取存档
|
||||
function loadWeekData() {
|
||||
try {
|
||||
@@ -106,87 +107,12 @@ function decreaseWeekTotal() {
|
||||
weekTotal = stored.weekTotal;
|
||||
}
|
||||
|
||||
// 查找文本
|
||||
async function findText(text, x, y, w, h, textAttempts = attempts) {
|
||||
const searchText = text.toLowerCase();
|
||||
|
||||
for (let i = 0; i < textAttempts; i++) {
|
||||
const captureRegion = captureGameRegion();
|
||||
const ro = RecognitionObject.ocr(x, y, w, h);
|
||||
const results = captureRegion.findMulti(ro);
|
||||
captureRegion.dispose();
|
||||
|
||||
for (let j = 0; j < results.count; j++) {
|
||||
const region = results[j];
|
||||
if (region.isExist() && region.text.toLowerCase().includes(searchText)) {
|
||||
return region;
|
||||
}
|
||||
}
|
||||
|
||||
await sleep(interval);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 升级特殊界面点击
|
||||
async function findAndClickWhiteSpaceNext() {
|
||||
const captureRegion = captureGameRegion();
|
||||
const ro = RecognitionObject.ocr(610, 950, 700, 60);
|
||||
const results = captureRegion.findMulti(ro);
|
||||
captureRegion.dispose();
|
||||
|
||||
for (let j = 0; j < results.count; j++) {
|
||||
const region = results[j];
|
||||
if (region.isExist()) {
|
||||
const text = region.text.toLowerCase();
|
||||
if (text.includes("点击") && text.includes("继续")) {
|
||||
await sleep(duration);
|
||||
click(610, 950);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找图片
|
||||
async function findImage(imgMat, x, y, w, h, imgAttempts = attempts) {
|
||||
const searchImg = RecognitionObject.TemplateMatch(imgMat, x, y, w, h);
|
||||
|
||||
for (let i = 0; i < imgAttempts; i++) {
|
||||
const captureRegion = captureGameRegion();
|
||||
const result = captureRegion.find(searchImg);
|
||||
captureRegion.dispose();
|
||||
|
||||
if (result.isExist()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
await sleep(interval);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 查找文本并点击
|
||||
async function findTextAndClick(text, x, y, w, h, textAttempts = attempts) {
|
||||
const target = await findText(text, x, y, w, h, textAttempts);
|
||||
if (target) {
|
||||
const result = await findText(["点击", "继续"],610, 950, 700, 60);
|
||||
if (result) {
|
||||
await sleep(duration);
|
||||
target.click();
|
||||
} else {
|
||||
log.error("文本{text}查找失败", text);
|
||||
}
|
||||
}
|
||||
|
||||
// 查找图片并点击
|
||||
async function findImageAndClick(path, x, y, w, h, imgAttempts = attempts) {
|
||||
const imgMat = getImgMat(path);
|
||||
const target = await findImage(imgMat, x, y, w, h, imgAttempts);
|
||||
if (target) {
|
||||
await sleep(duration);
|
||||
target.click();
|
||||
} else {
|
||||
log.error("图标{path}查找失败", path);
|
||||
click(610, 950);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +124,7 @@ async function findSaveInList(keyword) {
|
||||
const region = await findText(
|
||||
keyword,
|
||||
200, 250, 1500, 700,
|
||||
1
|
||||
3
|
||||
);
|
||||
|
||||
if (region) {
|
||||
@@ -227,8 +153,8 @@ async function deleteSource() {
|
||||
keyPress("VK_B");
|
||||
await sleep(duration);
|
||||
|
||||
await findTextAndClick("管理关卡", 960, 0, 960, 100);
|
||||
await findTextAndClick("管理", 960, 980, 960, 100);
|
||||
await findTextAndClick("管理关卡", 960, 0, 960, 100, 50, 50);
|
||||
await findTextAndClick("管理", 960, 980, 960, 100, 50, 50);
|
||||
|
||||
// 查找目标存档
|
||||
const saveRegion = await findSaveInList(starRoomName);
|
||||
@@ -242,12 +168,13 @@ async function deleteSource() {
|
||||
const sy = saveRegion.y - 30;
|
||||
|
||||
await sleep(300);
|
||||
await findImageAndClick("check_box", 0, sy, 1480, saveRegion.height + 70);
|
||||
const check_box = getImgMat("assets/check_box.png");
|
||||
await findImgAndClick(check_box, 0, sy, 1480, saveRegion.height + 70, 2000);
|
||||
// 删除
|
||||
await sleep(duration);
|
||||
await findTextAndClick("删除", 960, 980, 960, 100);
|
||||
await findTextAndClick("确认", 960, 600, 960, 400);
|
||||
await findTextAndClick("确认", 960, 600, 960, 400);
|
||||
await findTextAndClick("删除", 960, 980, 960, 100, 50, 100);
|
||||
await findTextAndClick("确认", 960, 600, 960, 400, 50, 100);
|
||||
await findTextAndClick("确认", 960, 600, 960, 400, 50, 100);
|
||||
|
||||
log.info("关卡存档删除完成");
|
||||
await sleep(duration);
|
||||
@@ -257,29 +184,48 @@ async function deleteSource() {
|
||||
// 进入千星奇域的全部奇域页面
|
||||
async function enterSourcePage() {
|
||||
// 1. 检测是否在房间内,在则退出
|
||||
const inRoom = await findText("房间", 1500, 0, 420, 500, 5);
|
||||
const inRoom = await findText("房间", 1500, 0, 420, 500, 5, 100);
|
||||
if (inRoom) {
|
||||
keyPress("VK_P");
|
||||
await sleep(duration);
|
||||
await findImageAndClick("exit_room", 960, 0, 960, 540);
|
||||
const exit_room = getImgMat("assets/exit_room.png");
|
||||
await waitUntilTextAppear(
|
||||
"确认",
|
||||
async () => {
|
||||
await findImgAndClick(exit_room, 960, 0, 960, 540, 500);
|
||||
},
|
||||
960,
|
||||
600,
|
||||
960,
|
||||
400,
|
||||
10
|
||||
);
|
||||
await findTextAndClick("确认", 960, 600, 960, 400);
|
||||
await genshin.returnMainUi();
|
||||
keyPress("VK_F6");
|
||||
} else {
|
||||
keyPress("VK_F6");
|
||||
}
|
||||
|
||||
await sleep(duration);
|
||||
}
|
||||
|
||||
// 进入千星奇域的收藏奇域页面
|
||||
async function enterStarSourcePage() {
|
||||
// 1. 检测是否在房间内,在则退出
|
||||
const inRoom = await findText("房间", 1500, 0, 420, 500, 5);
|
||||
const inRoom = await findText("房间", 1500, 0, 420, 500, 5, 100);
|
||||
if (inRoom) {
|
||||
keyPress("VK_P");
|
||||
await sleep(duration);
|
||||
await findImageAndClick("exit_room", 960, 0, 960, 540);
|
||||
const exit_room = getImgMat("assets/exit_room.png");
|
||||
await waitUntilTextAppear(
|
||||
"确认",
|
||||
async () => {
|
||||
await findImgAndClick(exit_room, 960, 0, 960, 540, 500);
|
||||
},
|
||||
960,
|
||||
600,
|
||||
960,
|
||||
400,
|
||||
100
|
||||
);
|
||||
await findTextAndClick("确认", 960, 600, 960, 400);
|
||||
await genshin.returnMainUi();
|
||||
keyPress("VK_B");
|
||||
@@ -302,20 +248,9 @@ async function createMap() {
|
||||
await sleep(duration);
|
||||
click(355, 365);
|
||||
await sleep(duration);
|
||||
while (true) {
|
||||
const result = await findText("房间", 960, 100, 960, 200, 2);
|
||||
if (result) {
|
||||
await sleep(duration);
|
||||
result.click();
|
||||
await sleep(duration);
|
||||
break;
|
||||
} else {
|
||||
const result2 = await findText("大厅", 960, 600, 960, 400, 2);
|
||||
if (result2) {
|
||||
result2.click();
|
||||
await sleep(duration);
|
||||
}
|
||||
}
|
||||
const result = await findTextAndClick("房间",960, 100, 960, 200, 2);
|
||||
if (!result) {
|
||||
await findTextAndClick("大厅", 960, 600, 960, 400, 2);
|
||||
}
|
||||
await findText("开始游戏", 960, 540, 960, 540);
|
||||
click(770, 275);
|
||||
@@ -326,25 +261,16 @@ async function createMap() {
|
||||
async function createStarMap() {
|
||||
await findTextAndClick("搜索", 0, 0, 1920, 120);
|
||||
inputText(roomID);
|
||||
await sleep(1000);
|
||||
await sleep(500);
|
||||
await findTextAndClick("搜索", 0, 0, 1920, 120);
|
||||
await sleep(duration);
|
||||
click(420, 830);
|
||||
await sleep(duration);
|
||||
while (true) {
|
||||
const result = await findText("房间", 960, 100, 960, 200, 2);
|
||||
if (result) {
|
||||
await sleep(duration);
|
||||
result.click();
|
||||
await sleep(duration);
|
||||
break;
|
||||
} else {
|
||||
const result2 = await findText("大厅", 960, 600, 960, 400, 2);
|
||||
if (result2) {
|
||||
result2.click();
|
||||
await sleep(duration);
|
||||
}
|
||||
}
|
||||
const result = await findTextAndClick("房间",960, 100, 960, 200, 2);
|
||||
if (!result) {
|
||||
await findTextAndClick("大厅", 960, 600, 960, 400, 2);
|
||||
await waitUntilTextAppear("房间", () => {},960, 100, 960, 200, 50, 1000);
|
||||
await findTextAndClick("房间",960, 100, 960, 200);
|
||||
}
|
||||
await findText("开始游戏", 960, 540, 960, 540);
|
||||
click(770, 275);
|
||||
@@ -363,49 +289,30 @@ async function playMap() {
|
||||
await createMap();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
await sleep(duration);
|
||||
const result = await findText("开始游戏", 960, 540, 960, 540, 5);
|
||||
if (result) {
|
||||
await sleep(duration);
|
||||
result.click();
|
||||
log.info("开始执行第{i}/{total}次奇域挑战", 1, total);
|
||||
await sleep(duration);
|
||||
} else {
|
||||
await sleep(duration);
|
||||
break;
|
||||
}
|
||||
}
|
||||
await findTextAndClick("开始游戏", 960, 540, 960, 540, 5, 50, 50);
|
||||
log.info("开始执行第{i}/{total}次奇域挑战", 1, total);
|
||||
let firstOutputCount = 0;
|
||||
while (true) {
|
||||
const whiteText = await findText("空白", 610, 900, 700, 100, 1);
|
||||
if (whiteText) {
|
||||
await sleep(duration);
|
||||
click(610, 950);
|
||||
}
|
||||
|
||||
const result = await findText("返回大厅", 960, 540, 960, 540, 1);
|
||||
if (result) {
|
||||
await sleep(duration);
|
||||
result.click();
|
||||
await sleep(duration);
|
||||
if (!useFixedAttempts) {
|
||||
decreaseWeekTotal();
|
||||
}
|
||||
log.info("本次关卡结束");
|
||||
break;
|
||||
} else {
|
||||
const inRoom = await findText("房间", 1500, 0, 420, 500, 1);
|
||||
if (inRoom) {
|
||||
break;
|
||||
}
|
||||
await waitUntilTextAppear(
|
||||
"返回大厅",
|
||||
async () => {
|
||||
await findAndClickWhiteSpaceNext();
|
||||
if (firstOutputCount % 16 === 0) {
|
||||
log.info("等待本次关卡结束...");
|
||||
}
|
||||
firstOutputCount++;
|
||||
await sleep(interval);
|
||||
}
|
||||
},
|
||||
960,
|
||||
540,
|
||||
960,
|
||||
540,
|
||||
500,
|
||||
2000
|
||||
);
|
||||
await findTextAndClick("返回大厅", 960, 540, 960, 540);
|
||||
if (!useFixedAttempts) {
|
||||
decreaseWeekTotal();
|
||||
}
|
||||
log.info("本次关卡结束");
|
||||
|
||||
await deleteSource();
|
||||
|
||||
@@ -415,42 +322,28 @@ async function playMap() {
|
||||
await sleep(duration);
|
||||
keyPress("VK_P");
|
||||
await sleep(duration);
|
||||
while (true) {
|
||||
const result = await findText("开始游戏", 960, 540, 960, 540, 1);
|
||||
if (result) {
|
||||
await sleep(duration);
|
||||
result.click();
|
||||
log.info("开始执行第{i}/{total}次奇域挑战", i + 1, total);
|
||||
await sleep(duration);
|
||||
break;
|
||||
} else {
|
||||
await sleep(duration);
|
||||
}
|
||||
}
|
||||
await findTextAndClick("开始游戏", 960, 540, 960, 540, 20, 50, 50);
|
||||
log.info("开始执行第{i}/{total}次奇域挑战", i + 1, total);
|
||||
let outputCount = 0;
|
||||
while (true) {
|
||||
await findAndClickWhiteSpaceNext();
|
||||
const result = await findText("返回大厅", 960, 540, 960, 540, 1);
|
||||
if (result) {
|
||||
await sleep(duration);
|
||||
result.click();
|
||||
await sleep(duration);
|
||||
if (!useFixedAttempts) {
|
||||
decreaseWeekTotal();
|
||||
}
|
||||
log.info("本次关卡结束");
|
||||
break;
|
||||
} else {
|
||||
const inRoom = await findText("房间", 1500, 0, 420, 500, 1);
|
||||
if (inRoom) {
|
||||
break;
|
||||
}
|
||||
if (outputCount % 10 === 0) {
|
||||
await waitUntilTextAppear(
|
||||
"返回大厅",
|
||||
async () => {
|
||||
await findAndClickWhiteSpaceNext();
|
||||
if (outputCount % 16 === 0) {
|
||||
log.info("等待本次关卡结束...");
|
||||
}
|
||||
outputCount++;
|
||||
await sleep(interval);
|
||||
}
|
||||
},
|
||||
960,
|
||||
540,
|
||||
960,
|
||||
540,
|
||||
500,
|
||||
2000
|
||||
);
|
||||
await findTextAndClick("返回大厅", 960, 540, 960, 540);
|
||||
if (!useFixedAttempts) {
|
||||
decreaseWeekTotal();
|
||||
}
|
||||
await deleteSource();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "千星奇域每周成就经验刷取",
|
||||
"version": "2.7",
|
||||
"bgi_version": "0.54.0",
|
||||
"description": "无需自己找图,可用于利用成就高经验值刷取经验,默认配置每周刷满需要24分钟",
|
||||
"version": "3.0",
|
||||
"bgi_version": "0.57.0",
|
||||
"description": "无需自己找图,可用于利用成就高经验值刷取经验,默认配置每周刷满需要22分钟,秒刷图仅需9分钟",
|
||||
"authors": [
|
||||
{
|
||||
"name": "躁动的氨气",
|
||||
|
||||
Reference in New Issue
Block a user