添加全新千星脚本 (#2416)

* 添加全新千星脚本

* 添加全新千星脚本

* 添加全新千星脚本

* 添加全新千星脚本
This commit is contained in:
躁动的氨气
2025-11-30 11:52:32 +08:00
committed by GitHub
parent 50052d37cc
commit f60e778486
9 changed files with 444 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
# 🌌 千星奇域 · 成就经验自动慢刷脚本
**⚠️ 提醒:由于奇域地图经常下架,默认地图也可能无法使用。若默认地图被下架,请使用 MiliastraExperienceAutomation 脚本刷取固定 20 经验的关卡,并将房间号填写到本脚本设置中。**
> 与 MiliastraExperienceAutomation 脚本存在部分逻辑重复,这里就不再重新撰写啦😇
---
## ❗ 使用前重要说明(免责声明)
本脚本依赖“**删除奇域地图存档**”来实现重复获取成就经验。
这意味着:
- 当脚本执行删除存档时,如果 **第一个存档不是你正在刷的地图**,可能会误删你正常游玩的关卡数据。
- 若你完全理解并接受此风险,请在脚本的 *自定义 JS 配置* 中手动勾选 `我已阅读说明中的免责声明` 才可继续执行脚本。
请务必确认你能承担潜在损失后再继续使用。
---
## 🌟 功能特点
- 🔁 自动重复通关指定奇域地图
- 🧹 自动删除存档,实现成就经验可重复获取
- 📅 自动监测每周经验上限,到达后自动停止
- 🏞️ 完成后自动返回提瓦特大陆,不影响其他自动化脚本
---
## 🛠️ 脚本设置项说明
| 配置项 | 描述 | 默认值 |
|--------------|---------------------------------------------------------------------|--------------|
| **runJS** | 我已阅读免责声明(未勾选无法执行) | `false` |
| **room** | 奇域关卡关键词或 GUID仅支持单个空则使用默认地图 | `37135473336` |
| **thisAttempts** | 指定通关次数(`0` = 无限,直到达到每周上限) | `0` |
| **weekMaxExp** | 每周可获取的经验值上限 | `4000` |
| **singleExp** | 每次通关可获得的经验值 | `270` |
---
## ❗ 注意事项
- 游戏窗口需保持 **16:9** 的宽高比例,否则可能影响图像识别。
- `singleExp` 请填写正确,否则脚本的剩余次数计算会不准确。
- 若默认地图下架,请手动输入可游玩的房间号。
---
## 📅 更新计划
未来可能会支持运行“收藏页面”中的奇域地图。
但由于收藏地图状态不稳定(部分下架 → 能进;彻底取消发布 → 无法进入),**暂不确定是否正式加入**。
---

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,5 @@
{
"weekMaxExp": 4000,
"singleExp": 270,
"weekTotal": 15
}

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

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

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