mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-31 05:59:51 +08:00
* 优化菈乌玛兽肉 * 自动修复 JSON 格式和版本号 [ci skip] --------- Co-authored-by: GitHub Actions Bot <actions@github.com>
380 lines
14 KiB
JavaScript
380 lines
14 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 hasKazuha = false;
|
||
|
||
if (avatars && avatars.length > 1) {
|
||
for (let i = 0; i < avatars.length; i++) {
|
||
if (avatars[i] === "菈乌玛") hasLayla = true;
|
||
if (avatars[i] === "枫原万叶") hasKazuha = true;
|
||
}
|
||
|
||
if (!hasLayla || !hasKazuha) {
|
||
log.error(`队伍角色检查失败 - 菈乌玛:${hasLayla ? '✓' : '✗'} 枫原万叶:${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 setTime(hour, minute) {
|
||
setGameMetrics(1920, 1080, 2); // 设置游戏窗口大小和DPI
|
||
try {
|
||
//拖动鼠标
|
||
async function moveMouseSmoothly(x1, y1, x2, y2) {
|
||
const deltaX = x2 - x1;
|
||
const deltaY = y2 - y1;
|
||
const steps = Math.max(Math.abs(deltaX), Math.abs(deltaY));
|
||
const stepX = deltaX / steps;
|
||
const stepY = deltaY / steps;
|
||
await moveMouseTo(x1, y1);
|
||
await leftButtonDown();
|
||
for (let i = 1; i <= steps; i++) {
|
||
const newX = x1 + stepX * i;
|
||
const newY = y1 + stepY * i;
|
||
const validX = Math.round(newX);
|
||
const validY = Math.round(newY);
|
||
await moveMouseTo(validX, validY);
|
||
await sleep(10);
|
||
}
|
||
await leftButtonUp();
|
||
}
|
||
//设定时间
|
||
async function settime(time) {
|
||
const centerX = 1441;
|
||
const centerY = 501;
|
||
const radius = 100;
|
||
let angle;
|
||
angle = (90 + time * 15) % 360;
|
||
angle = angle >= 0 ? angle : 360 + angle;
|
||
const angle1 = (angle + 90) % 360;
|
||
const angle2 = (angle + 180) % 360;
|
||
const angle3 = (angle + 270) % 360;
|
||
const radians = angle * (Math.PI / 180);
|
||
const radians1 = angle1 * (Math.PI / 180);
|
||
const radians2 = angle2 * (Math.PI / 180);
|
||
const radians3 = angle3 * (Math.PI / 180);
|
||
const x = centerX + radius * Math.cos(radians);
|
||
const y = centerY + radius * Math.sin(radians);
|
||
const x1 = centerX + radius * Math.cos(radians1);
|
||
const y1 = centerY + radius * Math.sin(radians1);
|
||
const x2 = centerX + radius * Math.cos(radians2);
|
||
const y2 = centerY + radius * Math.sin(radians2);
|
||
const x3 = centerX + radius * Math.cos(radians3);
|
||
const y3 = centerY + radius * Math.sin(radians3);
|
||
|
||
// 输出最终的坐标
|
||
await sleep(2000);
|
||
await moveMouseSmoothly(centerX,centerY, x1,y1);
|
||
await sleep(2000);
|
||
await moveMouseSmoothly(centerX,centerY, x2,y2);
|
||
await sleep(2000);
|
||
await moveMouseSmoothly(centerX,centerY, x3,y3);
|
||
await sleep(2000);
|
||
await moveMouseSmoothly(centerX,centerY, x,y);
|
||
}
|
||
//设置时间
|
||
log.info('设置时间到6点');
|
||
await sleep(1000)
|
||
await keyPress("Escape");
|
||
await sleep(1000);
|
||
await click(50,700);
|
||
await sleep(2000);
|
||
await settime(6)
|
||
await sleep(3000);
|
||
await click(1500,1000);//确认
|
||
await sleep(20000);
|
||
await keyPress("Escape");
|
||
await sleep(2000);
|
||
await keyPress("Escape");
|
||
await sleep(2000);
|
||
} finally {
|
||
// 恢复为脚本既定基准
|
||
setGameMetrics(1920, 1080, 1);
|
||
}
|
||
}
|
||
|
||
// 循环检测并调整潮湿状态
|
||
async function handleDampStatus() {
|
||
let attempts = 0;
|
||
while (attempts < maxWaitAttempts) {
|
||
if (await checkDampStatus()) {
|
||
log.info(`检测到潮湿状态,第${attempts + 1}次调整时间`);
|
||
await 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("所有路线运行完成");
|
||
})();
|