mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-17 03:43:26 +08:00
56
repo/js/WeeklyThousandStarRealm/README.md
Normal file
56
repo/js/WeeklyThousandStarRealm/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# 🌌 千星奇域 · 成就经验自动慢刷脚本
|
||||
|
||||
**⚠️ 提醒:由于奇域地图经常下架,默认地图也可能无法使用。若默认地图被下架,请使用 MiliastraExperienceAutomation 脚本刷取固定 20 经验的关卡,并将房间号填写到本脚本设置中。**
|
||||
|
||||
> 与 MiliastraExperienceAutomation 脚本存在部分逻辑重复,这里就不再重新撰写啦😇
|
||||
|
||||
---
|
||||
|
||||
## ❗ 使用前重要说明(免责声明)
|
||||
|
||||
本脚本依赖“**删除奇域地图存档**”来实现重复获取成就经验。
|
||||
|
||||
这意味着:
|
||||
|
||||
- 当脚本执行删除存档时,如果 **第一个存档不是你正在刷的地图**,可能会误删你正常游玩的关卡数据。
|
||||
- 若你完全理解并接受此风险,请在脚本的 *自定义 JS 配置* 中手动勾选 `我已阅读说明中的免责声明` 才可继续执行脚本。
|
||||
|
||||
请务必确认你能承担潜在损失后再继续使用。
|
||||
|
||||
---
|
||||
|
||||
## 🌟 功能特点
|
||||
|
||||
- 🔁 自动重复通关指定奇域地图
|
||||
- 🧹 自动删除存档,实现成就经验可重复获取
|
||||
- 📅 自动监测每周经验上限,到达后自动停止
|
||||
- 🏞️ 完成后自动返回提瓦特大陆,不影响其他自动化脚本
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 脚本设置项说明
|
||||
|
||||
| 配置项 | 描述 | 默认值 |
|
||||
|--------------|---------------------------------------------------------------------|--------------|
|
||||
| **runJS** | 我已阅读免责声明(未勾选无法执行) | `false` |
|
||||
| **room** | 奇域关卡关键词或 GUID(仅支持单个;空则使用默认地图) | `37135473336` |
|
||||
| **thisAttempts** | 指定通关次数(`0` = 无限,直到达到每周上限) | `0` |
|
||||
| **weekMaxExp** | 每周可获取的经验值上限 | `4000` |
|
||||
| **singleExp** | 每次通关可获得的经验值 | `270` |
|
||||
|
||||
---
|
||||
|
||||
## ❗ 注意事项
|
||||
|
||||
- 游戏窗口需保持 **16:9** 的宽高比例,否则可能影响图像识别。
|
||||
- `singleExp` 请填写正确,否则脚本的剩余次数计算会不准确。
|
||||
- 若默认地图下架,请手动输入可游玩的房间号。
|
||||
|
||||
---
|
||||
|
||||
## 📅 更新计划
|
||||
|
||||
未来可能会支持运行“收藏页面”中的奇域地图。
|
||||
但由于收藏地图状态不稳定(部分下架 → 能进;彻底取消发布 → 无法进入),**暂不确定是否正式加入**。
|
||||
|
||||
---
|
||||
BIN
repo/js/WeeklyThousandStarRealm/assets/check_box.png
Normal file
BIN
repo/js/WeeklyThousandStarRealm/assets/check_box.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 473 B |
BIN
repo/js/WeeklyThousandStarRealm/assets/close_manage.png
Normal file
BIN
repo/js/WeeklyThousandStarRealm/assets/close_manage.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
repo/js/WeeklyThousandStarRealm/assets/close_star_manage.png
Normal file
BIN
repo/js/WeeklyThousandStarRealm/assets/close_star_manage.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
BIN
repo/js/WeeklyThousandStarRealm/assets/exit_room.png
Normal file
BIN
repo/js/WeeklyThousandStarRealm/assets/exit_room.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
5
repo/js/WeeklyThousandStarRealm/data/store.json
Normal file
5
repo/js/WeeklyThousandStarRealm/data/store.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"weekMaxExp": 4000,
|
||||
"singleExp": 270,
|
||||
"weekTotal": 15
|
||||
}
|
||||
334
repo/js/WeeklyThousandStarRealm/main.js
Normal file
334
repo/js/WeeklyThousandStarRealm/main.js
Normal file
@@ -0,0 +1,334 @@
|
||||
const attempts = 20; // 最大尝试次数
|
||||
const interval = 1000; // 每次间隔 ms
|
||||
const duration = 1000; // 默认点击等待延时
|
||||
|
||||
const storePath = "data/store.json"
|
||||
const runJS = settings.runJS || false;
|
||||
const roomID = settings.room || "37135473336";
|
||||
const userAttempts = Number(settings.thisAttempts || "0");
|
||||
const useFixedAttempts = userAttempts > 0;
|
||||
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 {
|
||||
const t = file.readTextSync(storePath);
|
||||
return JSON.parse(t);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存存档
|
||||
function saveWeekData(data) {
|
||||
file.writeTextSync(storePath, JSON.stringify(data));
|
||||
}
|
||||
|
||||
// 初始化或更新 weekTotal
|
||||
function initWeekTotal() {
|
||||
const stored = loadWeekData();
|
||||
const calculated = Math.ceil(weekMaxExp / singleExp);
|
||||
|
||||
// 首次 OR 配置变化 → 重写
|
||||
if (
|
||||
!stored ||
|
||||
stored.weekMaxExp !== weekMaxExp ||
|
||||
stored.singleExp !== singleExp
|
||||
) {
|
||||
const newData = {
|
||||
weekMaxExp,
|
||||
singleExp,
|
||||
weekTotal: calculated
|
||||
};
|
||||
saveWeekData(newData);
|
||||
return calculated;
|
||||
}
|
||||
|
||||
return stored.weekTotal;
|
||||
}
|
||||
|
||||
// 刷完一次 → 计数 -1
|
||||
function decreaseWeekTotal() {
|
||||
const stored = loadWeekData();
|
||||
if (!stored) return;
|
||||
|
||||
stored.weekTotal = Math.max(stored.weekTotal - 1, 0);
|
||||
saveWeekData(stored);
|
||||
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 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 清除游玩数据
|
||||
async function deleteSource() {
|
||||
await sleep(duration);
|
||||
await genshin.returnMainUi();
|
||||
log.info("开始清除地图数据");
|
||||
await sleep(duration);
|
||||
keyPress("VK_B");
|
||||
await sleep(duration);
|
||||
await findTextAndClick("管理关卡", 960, 0, 960, 100);
|
||||
await findTextAndClick("管理", 960, 980, 960, 100);
|
||||
await findImageAndClick("check_box", 0, 0, 1480, 340);
|
||||
await findTextAndClick("删除",960, 980, 960, 100);
|
||||
await findTextAndClick("确认", 960, 600, 960, 400);
|
||||
await findTextAndClick("确认", 960, 600, 960, 400);
|
||||
log.info("数据清除完成");
|
||||
await sleep(duration);
|
||||
await genshin.returnMainUi();
|
||||
}
|
||||
|
||||
// 进入千星奇域的全部奇域页面
|
||||
async function enterSourcePage() {
|
||||
// 1. 检测是否在房间内,在则退出
|
||||
const inRoom = await findText("房间", 1500, 0, 420, 500, 5);
|
||||
if (inRoom) {
|
||||
keyPress("VK_P");
|
||||
await sleep(duration);
|
||||
await findImageAndClick("exit_room", 960, 0, 960, 540);
|
||||
await findTextAndClick("确认", 960, 600, 960, 400);
|
||||
await genshin.returnMainUi();
|
||||
keyPress("VK_F6");
|
||||
} else {
|
||||
keyPress("VK_F6");
|
||||
}
|
||||
|
||||
await sleep(duration);
|
||||
}
|
||||
|
||||
// 创建关卡
|
||||
async function createMap() {
|
||||
await findTextAndClick("全部", 1320, 0, 600, 95);
|
||||
await findTextAndClick("搜索", 0, 120, 1920, 60);
|
||||
inputText(roomID);
|
||||
await sleep(1000);
|
||||
await findTextAndClick("搜索", 0, 120, 1920, 60);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
await findText("开始游戏", 960, 540, 960, 540);
|
||||
click(770, 275);
|
||||
await sleep(duration);
|
||||
}
|
||||
|
||||
// 游玩关卡
|
||||
async function playMap() {
|
||||
const stored = loadWeekData();
|
||||
const leave = stored ? stored.weekTotal : weekTotal;
|
||||
const total = useFixedAttempts ? userAttempts : leave;
|
||||
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;
|
||||
}
|
||||
}
|
||||
let firstOutputCount = 0;
|
||||
while (true) {
|
||||
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 (firstOutputCount % 16 === 0) {
|
||||
log.info("等待本次关卡结束...");
|
||||
}
|
||||
firstOutputCount++;
|
||||
await sleep(interval);
|
||||
}
|
||||
}
|
||||
|
||||
await deleteSource();
|
||||
|
||||
for (let i = 1; i < total; i++) {
|
||||
const inRoom = await findText("房间", 1500, 0, 420, 500);
|
||||
if (inRoom) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
let outputCount = 0;
|
||||
while (true) {
|
||||
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) {
|
||||
log.info("等待本次关卡结束...");
|
||||
}
|
||||
outputCount++;
|
||||
await sleep(interval);
|
||||
}
|
||||
}
|
||||
await deleteSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 退出到提瓦特
|
||||
async function exitToTeyvat() {
|
||||
await genshin.returnMainUi();
|
||||
await sleep(duration);
|
||||
keyPress("VK_F2");
|
||||
await sleep(duration);
|
||||
await findTextAndClick("返回", 960, 0, 960, 100);
|
||||
await sleep(duration);
|
||||
await findTextAndClick("确认", 960, 600, 960, 400);
|
||||
}
|
||||
|
||||
(async function () {
|
||||
if (!runJS) {
|
||||
log.error("您未同意此脚本的免责声明,请先同意后重新运行此脚本!");
|
||||
return;
|
||||
}
|
||||
|
||||
await genshin.returnMainUi();
|
||||
|
||||
if (useFixedAttempts) {
|
||||
// 手动模式:忽略本周计数
|
||||
log.info(
|
||||
"已进入指定次数模式,本次将执行{count}次挑战(不计入周进度)",
|
||||
userAttempts
|
||||
);
|
||||
} else {
|
||||
// 每周模式
|
||||
const stored = loadWeekData();
|
||||
const leave = stored.weekTotal;
|
||||
const done = Math.ceil(weekMaxExp / singleExp) - leave;
|
||||
|
||||
log.info(
|
||||
"本周共需刷取 {total} 次,已刷 {done} 次,剩余 {leave} 次",
|
||||
Math.ceil(weekMaxExp / singleExp),
|
||||
done,
|
||||
leave
|
||||
);
|
||||
}
|
||||
|
||||
await enterSourcePage();
|
||||
await playMap();
|
||||
await exitToTeyvat();
|
||||
})();
|
||||
18
repo/js/WeeklyThousandStarRealm/manifest.json
Normal file
18
repo/js/WeeklyThousandStarRealm/manifest.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "千星奇域每周成就经验刷取",
|
||||
"version": "1.0",
|
||||
"bgi_version": "0.53.0",
|
||||
"description": "可用于利用成就高经验值刷取经验",
|
||||
"authors": [
|
||||
{
|
||||
"name": "躁动的氨气",
|
||||
"link": "https://github.com/zaodonganqi"
|
||||
}
|
||||
],
|
||||
"main": "main.js",
|
||||
"settings_ui": "settings.json",
|
||||
"saved_files": [
|
||||
"store/*.json"
|
||||
]
|
||||
}
|
||||
31
repo/js/WeeklyThousandStarRealm/settings.json
Normal file
31
repo/js/WeeklyThousandStarRealm/settings.json
Normal file
@@ -0,0 +1,31 @@
|
||||
[
|
||||
{
|
||||
"name": "runJS",
|
||||
"type": "checkbox",
|
||||
"label": "我已阅读说明中的免责声明"
|
||||
},
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "room",
|
||||
"label": "奇域关卡关键词或关卡GUID\n(仅支持单个,默认为作者特供地图)",
|
||||
"default": "37135473336"
|
||||
},
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "thisAttempts",
|
||||
"label": "指定通关次数",
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "weekMaxExp",
|
||||
"label": "每周可获取的经验值上限",
|
||||
"default": "4000"
|
||||
},
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "singleExp",
|
||||
"label": "每次通关获取的经验值数量",
|
||||
"default": "270"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user