mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-25 04:59:52 +08:00
* 菈乌玛兽肉支持少女 * 自动修复 JSON 格式和版本号 [ci skip] --------- Co-authored-by: GitHub Actions Bot <actions@github.com>
306 lines
11 KiB
JavaScript
306 lines
11 KiB
JavaScript
(async function () {
|
||
setGameMetrics(1920, 1080, 1);
|
||
|
||
// 读取用户设置
|
||
const enableDampDetection = settings.enableDampDetection === true; // 默认启用
|
||
const maxWaitAttempts = parseInt(settings.maxWaitAttempts) || 5; // 转换数字输入
|
||
const enableHighRiskRoutes = settings.enableHighRiskRoutes === true; // 默认启用高危路线
|
||
const partyName = settings.partyName || ""; // 队伍名称
|
||
|
||
// 检测当前队伍角色
|
||
function checkRequiredCharacters() {
|
||
const avatars = getAvatars();
|
||
let hasLayla = false;
|
||
let hasColumbina = false;
|
||
let hasKazuha = false;
|
||
|
||
if (avatars && avatars.length > 1) {
|
||
for (let i = 0; i < avatars.length; i++) {
|
||
if (avatars[i] === "菈乌玛") hasLayla = true;
|
||
if (avatars[i] === "哥伦比娅") hasColumbina = true;
|
||
if (avatars[i] === "枫原万叶") hasKazuha = true;
|
||
}
|
||
|
||
const hasOne = hasLayla || hasColumbina;
|
||
|
||
if (!hasOne || !hasKazuha) {
|
||
log.error(`队伍角色检查失败 - 菈乌玛或哥伦比娅:${hasOne ? '✓' : '✗'} 枫原万叶:${hasKazuha ? '✓' : '✗'}`);
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 高危路线数组(目前只有07路线)
|
||
const highRiskRoutes = ["07"];
|
||
|
||
log.info(`兽肉路线脚本启动 - 潮湿检测:${enableDampDetection ? '启用' : '禁用'} 高危路线:${enableHighRiskRoutes ? '启用' : '禁用'} 队伍:${partyName || '未设置'}`);
|
||
|
||
// 切换队伍函数
|
||
async function switchPartyIfNeeded(partyName) {
|
||
if (!partyName) {
|
||
await genshin.returnMainUi();
|
||
return;
|
||
}
|
||
try {
|
||
partyName = partyName.trim();
|
||
log.info("切换队伍: " + partyName);
|
||
if (!await genshin.switchParty(partyName)) {
|
||
log.info("切换失败,前往七天神像重试");
|
||
await genshin.tpToStatueOfTheSeven();
|
||
await genshin.switchParty(partyName);
|
||
}
|
||
} catch {
|
||
log.error("队伍切换失败");
|
||
notification.error(`队伍切换失败`);
|
||
await genshin.returnMainUi();
|
||
}
|
||
}
|
||
|
||
// 读取文件夹中的所有文件
|
||
async function readFolder(folderPath, onlyJson) {
|
||
const folderStack = [folderPath];
|
||
const files = [];
|
||
|
||
while (folderStack.length > 0) {
|
||
const currentPath = folderStack.pop();
|
||
|
||
// 读取当前路径下的所有文件和子文件夹路径
|
||
const filesInSubFolder = file.ReadPathSync(currentPath);
|
||
|
||
// 临时数组,用于存储子文件夹路径
|
||
const subFolders = [];
|
||
for (const filePath of filesInSubFolder) {
|
||
if (file.IsFolder(filePath)) {
|
||
// 如果是文件夹,先存储到临时数组中
|
||
subFolders.push(filePath);
|
||
} else {
|
||
if (filePath.endsWith(".js")) {
|
||
//跳过js结尾的文件
|
||
continue;
|
||
}
|
||
// 如果是文件,根据 onlyJson 判断是否存储
|
||
if (onlyJson) {
|
||
if (filePath.endsWith(".json")) {
|
||
const fileName = filePath.split('\\').pop(); // 提取文件名
|
||
const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组
|
||
files.push({
|
||
fullPath: filePath,
|
||
fileName: fileName,
|
||
folderPathArray: folderPathArray
|
||
});
|
||
}
|
||
} else {
|
||
const fileName = filePath.split('\\').pop(); // 提取文件名
|
||
const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组
|
||
files.push({
|
||
fullPath: filePath,
|
||
fileName: fileName,
|
||
folderPathArray: folderPathArray
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// 将子文件夹路径添加到堆栈中
|
||
for (let i = subFolders.length - 1; i >= 0; i--) {
|
||
folderStack.push(subFolders[i]);
|
||
}
|
||
}
|
||
|
||
return files;
|
||
}
|
||
|
||
// 动态获取兽肉路线文件
|
||
async function getMeatRoutes() {
|
||
try {
|
||
// 使用与锄地一条龙相同的方式读取路径文件
|
||
const pathsDir = "paths";
|
||
const allRoutes = [];
|
||
const routeOptions = [];
|
||
|
||
// 读取路径文件夹中的所有文件
|
||
const pathings = await readFolder(pathsDir, true);
|
||
|
||
for (const pathing of pathings) {
|
||
const fullPath = pathing.fullPath;
|
||
const fileName = pathing.fileName;
|
||
|
||
allRoutes.push(fullPath);
|
||
|
||
// 提取路线ID和名称用于选项
|
||
const routeId = fileName.match(/^(\d+)/)?.[1];
|
||
if (routeId) {
|
||
const routeName = fileName.replace('.json', '');
|
||
routeOptions.push({
|
||
value: routeId,
|
||
label: routeName
|
||
});
|
||
}
|
||
}
|
||
|
||
return allRoutes;
|
||
} catch (err) {
|
||
log.error("获取路线文件时出错:", err);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// 根据设置选择要运行的路线
|
||
const allRoutes = await getMeatRoutes();
|
||
let meatRoutes = allRoutes.filter(route => {
|
||
// 从路径中提取路线ID(假设文件名以数字开头)
|
||
const fileName = route.split('\\').pop(); // 使用反斜杠分割,因为Windows路径
|
||
const routeId = fileName.match(/^(\d+)/)?.[1];
|
||
|
||
if (!routeId) return false;
|
||
|
||
// 如果是高危路线,需要检查是否启用高危路线
|
||
if (highRiskRoutes.includes(routeId)) {
|
||
return enableHighRiskRoutes;
|
||
}
|
||
|
||
// 非高危路线默认运行
|
||
return true;
|
||
});
|
||
|
||
// 检查是否找到路线文件
|
||
if (meatRoutes.length === 0) {
|
||
log.error("未找到任何路线文件!请确保paths目录下存在路线文件。");
|
||
return;
|
||
}
|
||
|
||
log.info(`将运行 ${meatRoutes.length} 条路线`);
|
||
|
||
// 检测潮湿状态
|
||
async function checkDampStatus() {
|
||
try {
|
||
// 读取模板图像
|
||
const templateImage = file.ReadImageMatSync("assets/damp.png");
|
||
if (!templateImage) {
|
||
log.warn("无法读取潮湿状态模板图像");
|
||
return false;
|
||
}
|
||
|
||
// 创建模板匹配对象
|
||
const dampRo = RecognitionObject.TemplateMatch(templateImage, 700, 880, 260, 200);
|
||
dampRo.threshold = 0.8;
|
||
dampRo.InitTemplate();
|
||
|
||
const gameRegion = captureGameRegion();
|
||
// 执行模板匹配
|
||
const result = gameRegion.find(dampRo);
|
||
gameRegion.dispose();
|
||
|
||
return result.isExist();
|
||
} catch (err) {
|
||
log.error("检测潮湿状态时出错:", err.message || err);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 循环检测并调整潮湿状态
|
||
async function handleDampStatus() {
|
||
let attempts = 0;
|
||
while (attempts < maxWaitAttempts) {
|
||
if (await checkDampStatus()) {
|
||
log.info(`检测到潮湿状态,第${attempts + 1}次调整时间`);
|
||
await genshin.setTime(6, 0);
|
||
await sleep(3000); // 等待调时间生效
|
||
} else {
|
||
log.info("潮湿状态已解除");
|
||
return true;
|
||
}
|
||
attempts++;
|
||
}
|
||
log.warn("潮湿状态调整超时");
|
||
return false;
|
||
}
|
||
|
||
// 传送到路线第一个点位
|
||
async function teleportToFirstPoint(routePath) {
|
||
try {
|
||
// 读取路线文件获取第一个点位坐标
|
||
const routeContent = file.readTextSync(routePath);
|
||
const routeData = JSON.parse(routeContent);
|
||
if (!routeData || !routeData.positions || routeData.positions.length === 0) {
|
||
log.error(`路线文件格式错误: ${routePath}`);
|
||
return false;
|
||
}
|
||
|
||
const firstPoint = routeData.positions[0];
|
||
if (typeof firstPoint.x !== "number" || typeof firstPoint.y !== "number"
|
||
|| Number.isNaN(firstPoint.x) || Number.isNaN(firstPoint.y)) {
|
||
log.error(`坐标无效: ${routePath}`);
|
||
return false;
|
||
}
|
||
|
||
// 使用传送功能传送到第一个点位
|
||
await genshin.tp(firstPoint.x, firstPoint.y);
|
||
await sleep(2000); // 等待传送完成
|
||
|
||
return true;
|
||
} catch (err) {
|
||
log.error(`传送失败 ${routePath}:`, err);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 运行单条兽肉路线
|
||
async function runMeatRoute(routePath) {
|
||
try {
|
||
// 运行路径追踪脚本
|
||
await pathingScript.runFile(routePath);
|
||
} catch (err) {
|
||
log.error(`路线运行失败 ${routePath}:`, err);
|
||
}
|
||
}
|
||
|
||
// 主执行逻辑
|
||
// 设置游戏分辨率和DPI缩放
|
||
setGameMetrics(1920, 1080, 1);
|
||
|
||
// 切换队伍
|
||
await switchPartyIfNeeded(partyName);
|
||
|
||
// 检查队伍角色
|
||
if (!checkRequiredCharacters()) {
|
||
log.error("队伍角色检查失败,脚本终止");
|
||
return;
|
||
}
|
||
|
||
// 开启自动拾取
|
||
dispatcher.addTimer(new RealtimeTimer("AutoPick"));
|
||
|
||
for (let i = 0; i < meatRoutes.length; i++) {
|
||
const route = meatRoutes[i];
|
||
log.info(`路线 ${i + 1}/${meatRoutes.length}: ${route.split('\\').pop()}`);
|
||
|
||
// 先传送到路线的第一个点位
|
||
const teleportSuccess = await teleportToFirstPoint(route);
|
||
if (!teleportSuccess) {
|
||
log.error(`传送失败,跳过路线`);
|
||
continue;
|
||
}
|
||
|
||
// 检测潮湿状态
|
||
if (enableDampDetection) {
|
||
await sleep(2000);
|
||
if (await checkDampStatus()) {
|
||
await handleDampStatus();
|
||
}
|
||
}
|
||
|
||
// 运行路线
|
||
await runMeatRoute(route);
|
||
|
||
// 路线间等待
|
||
if (i < meatRoutes.length - 1) {
|
||
await sleep(2000);
|
||
}
|
||
}
|
||
|
||
log.info("所有路线运行完成");
|
||
})();
|