Files
bettergi-scripts-list/repo/js/AutoCommission/main.js
LX666-666 95f9034cfd 自动委托0.97.1 (#1105)
* 战斗委托

* Delete repo/js/AutoCommission/丘丘人的一小步 directory

* Delete repo/js/AutoCommission/临危受命 directory

* Delete repo/js/AutoCommission/为了执行官大人 directory

* Delete repo/js/AutoCommission/冰凉凉的大团危机 directory

* Delete repo/js/AutoCommission/冷冰冰的大麻烦 directory

* Delete repo/js/AutoCommission/固若金汤 directory

* Delete repo/js/AutoCommission/圆滚滚的易爆品 directory

* Delete repo/js/AutoCommission/安全运输法则 directory

* Delete repo/js/AutoCommission/持盾的危机 directory

* Delete repo/js/AutoCommission/邪恶的扩张 directory

* Delete repo/js/AutoCommission/攀高危险 directory

* Delete repo/js/AutoCommission/极速前进 directory

* 战斗委托

* 对话委托

* 自动委托0.97.1
2025-06-15 22:25:05 +08:00

1869 lines
62 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// V0.97.1
(async function () {
// 定义常量
const OCR_REGION_X = 750;
const OCR_REGION_Y = 250;
const OCR_REGION_WIDTH = 450; // 1200 - 750
const OCR_REGION_HEIGHT = 400; // 650 - 250
// 修复文件路径问题 - 使用相对路径
const SUPPORT_LIST_PATH = "name.json";
const OUTPUT_DIR = "Data";
// 添加图像识别相关常量
const COMPLETED_IMAGE_PATH = "Data/RecognitionObject/Completed.png";
const UNCOMPLETED_IMAGE_PATH = "Data/RecognitionObject/UnCompleted.png";
// 委托类型常量
const COMMISSION_TYPE = {
FIGHT: "fight",
TALK: "talk",
};
// 对话委托流程路径
const TALK_PROCESS_BASE_PATH = "assets/process";
// 委托详情按钮位置(扩展检测范围左右+50像素
const COMMISSION_DETAIL_BUTTONS = [
{ id: 1, x: 1550, y: 320, checkX: 1450, checkWidth: 150 }, // 第一个委托详情按钮
{ id: 2, x: 1550, y: 420, checkX: 1450, checkWidth: 150 }, // 第二个委托详情按钮
{ id: 3, x: 1550, y: 530, checkX: 1500, checkWidth: 100 }, // 第三个委托详情按钮
{ id: 4, x: 1550, y: 560, checkX: 1450, checkWidth: 150 }, // 第四个委托详情按钮(滑动后)
];
// 委托地点OCR区域
const LOCATION_OCR_X = 1530;
const LOCATION_OCR_Y = 100;
const LOCATION_OCR_WIDTH = 250; // 1630 - 1530
const LOCATION_OCR_HEIGHT = 30; // 130 - 100
// 委托触发检测区域
const COMMISSION_TRIGGER_OCR_X = 885;
const COMMISSION_TRIGGER_OCR_Y = 200;
const COMMISSION_TRIGGER_OCR_WIDTH = 165; // 1050 - 885
const COMMISSION_TRIGGER_OCR_HEIGHT = 50; // 250 - 200
// 委托完成检测区域
const COMMISSION_COMPLETE_OCR_X = 880;
const COMMISSION_COMPLETE_OCR_Y = 165;
const COMMISSION_COMPLETE_OCR_WIDTH = 170; // 1050 - 880
const COMMISSION_COMPLETE_OCR_HEIGHT = 45; // 210 - 165
// 获取设置
const skipRecognition = settings.skipRecognition || false;
//const debugMode = settings.debugMode || false;
const minTextLength = parseInt(settings.minTextLength || "4");
const team = settings.team || "";
// 存储当前委托位置
let currentCommissionPosition = null;
async function prepareForLeyLineRun() {
// 开局传送到七天神像
await genshin.returnMainUi();
await genshin.tpToStatueOfTheSeven();
// 切换战斗队伍
if (team) {
log.info(`切换至队伍 ${team}`);
await genshin.switchParty(team);
}
}
/**
* 自动导航到NPC对话位置
* @param {string} npcName - 目标NPC名称
* @param {string} iconType - 图标类型 ("task"或"bigmap")
* @returns {Promise<void>}
*/
const autoNavigateToTalk = async (npcName = "", iconType = "") => {
// 设置目标NPC名称
const textArray = [npcName];
//log.info(npcName + iconType);
// 根据图标类型选择不同的识别对象
let boxIconRo;
if (iconType === "task") {
boxIconRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync("Data/RecognitionObject/IconTaskCommission.png")
);
log.info("使用任务图标");
} else { // 默认使用大地图图标
boxIconRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync("Data/RecognitionObject/IconBigmapCommission.jpg")
);
}
const rewardTextRo = RecognitionObject.Ocr(1210, 515, 200, 50); //对话区域检测
let advanceNum = 0; //前进次数
middleButtonClick();
await sleep(800);
while (true) {
// 1. 优先检查是否已到达
let captureRegion = captureGameRegion();
let rewardTextArea = captureRegion.DeriveCrop(1210, 515, 200, 50);
let rewardResult = rewardTextArea.find(RecognitionObject.ocrThis);
// 检测到特点文字则结束!!!
if (rewardResult.text == textArray[0]) {
log.info("已到达指定位置,检测到文字: " + rewardResult.text);
return;
} else if (advanceNum > 80) {
throw new Error("前进时间超时");
}
// 2. 未到达领奖点,则调整视野
for (let i = 0; i < 100; i++) {
captureRegion = captureGameRegion();
let iconRes = captureRegion.Find(boxIconRo);
log.info(
"检测到委托图标位置 ({x}, {y})",
iconRes.x,
iconRes.y
)
let climbTextArea = captureRegion.DeriveCrop(1808, 1030, 25, 25);
let climbResult = climbTextArea.find(RecognitionObject.ocrThis);
// 检查是否处于攀爬状态
if (climbResult.isEmpty()) {
log.info("检侧进入攀爬状态,尝试脱离");
keyPress("x");
await sleep(1000);
keyDown("a");
await sleep(800);
keyUp("a");
keyDown("w");
await sleep(800);
keyUp("w");
}
if (iconRes.x >= 920 && iconRes.x <= 980 && iconRes.y <= 540) {
advanceNum++;
log.info(`视野已调正,前进第${advanceNum}`);
break;
} else {
// 小幅度调整
if (iconRes.y >= 520) moveMouseBy(0, 920);
let adjustAmount = iconRes.x < 920 ? -20 : 20;
let distanceToCenter = Math.abs(iconRes.x - 920); // 计算与920的距离
let scaleFactor = Math.max(1, Math.floor(distanceToCenter / 50)); // 根据距离缩放最小为1
let adjustAmount2 = iconRes.y < 540 ? scaleFactor : 10;
moveMouseBy(adjustAmount * adjustAmount2, 0);
await sleep(100);
}
if (i > 50) throw new Error("视野调整超时");
}
// 3. 前进一小步
keyDown("w");
await sleep(500);
keyUp("w");
await sleep(200); // 等待角色移动稳定
}
};
/**
* 检测委托完成状态(使用图像识别)
* @param {number} buttonIndex - 按钮索引0-3
* @returns {Promise<string>} 返回 "completed", "uncompleted", 或 "unknown"
*/
async function detectCommissionStatusByImage(buttonIndex) {
try {
const button = COMMISSION_DETAIL_BUTTONS[buttonIndex];
if (!button) {
log.error("无效的按钮索引: {index}", buttonIndex);
return "unknown";
}
//log.info("检测委托{id}的完成状态(图像识别)", button.id);
// 截图
let captureRegion = captureGameRegion();
// 检测区域:按钮位置左右各扩展更大范围
const checkRegion = captureRegion.deriveCrop(
button.checkX,
button.y - 30, // 稍微向上扩展检测区域
button.checkWidth,
60 // 增加高度以确保捕获状态图标
);
// 加载完成和未完成的模板图像
let completedTemplate, uncompletedTemplate;
try {
completedTemplate = file.readImageMatSync(COMPLETED_IMAGE_PATH);
uncompletedTemplate = file.readImageMatSync(UNCOMPLETED_IMAGE_PATH);
} catch (imageError) {
log.error("加载模板图像失败: {error}", imageError);
return "unknown";
}
// 创建识别对象,使用更灵活的参数
const completedRo = RecognitionObject.TemplateMatch(
completedTemplate,
0,
0,
button.checkWidth,
60
);
const uncompletedRo = RecognitionObject.TemplateMatch(
uncompletedTemplate,
0,
0,
button.checkWidth,
60
);
// 降低匹配阈值,提高识别灵活性
completedRo.threshold = 0.65;
uncompletedRo.threshold = 0.65;
// 检测完成状态
const completedResult = checkRegion.find(completedRo);
if (!completedResult.isEmpty()) {
//log.info("委托{id}已完成", button.id);
return "completed";
}
// 检测未完成状态
const uncompletedResult = checkRegion.find(uncompletedRo);
if (!uncompletedResult.isEmpty()) {
//log.info("委托{id}未完成", button.id);
return "uncompleted";
}
/* 尝试使用更低的阈值再次检测
log.info("使用更低阈值再次检测委托{id}状态", button.id);
completedRo.threshold = 0.6;
uncompletedRo.threshold = 0.6;
const completedResult2 = checkRegion.find(completedRo);
if (!completedResult2.isEmpty()) {
log.info("委托{id}已完成(低阈值检测)", button.id);
return "completed";
}
const uncompletedResult2 = checkRegion.find(uncompletedRo);
if (!uncompletedResult2.isEmpty()) {
log.info("委托{id}未完成(低阈值检测)", button.id);
return "uncompleted";
}*/
log.warn("委托{id}状态识别失败", button.id);
return "unknown";
} catch (error) {
log.error("检测委托完成状态时出错: {error}", error);
return "unknown";
}
}
// 读取支持的委托列表
function loadSupportedCommissions() {
let supportedCommissions = {
fight: [],
talk: [],
};
try {
// 使用正确的文件读取方法
log.info("开始读取支持的委托列表: {path}", SUPPORT_LIST_PATH);
// 尝试读取文件内容
try {
const supportListContent = file.readTextSync(SUPPORT_LIST_PATH);
if (supportListContent && supportListContent.trim()) {
try {
// 解析JSON格式
const commissionData = JSON.parse(supportListContent);
supportedCommissions.fight = commissionData.fight || [];
supportedCommissions.talk = commissionData.talk || [];
log.info(
"已加载支持的战斗委托列表,共 {count} 个",
supportedCommissions.fight.length
);
log.info(
"已加载支持的对话委托列表,共 {count} 个",
supportedCommissions.talk.length
);
} catch (jsonError) {
log.error("解析委托列表JSON失败: {error}", jsonError);
}
} else {
log.warn("支持的委托列表为空");
}
} catch (readError) {
// 如果读取失败,检查文件是否存在
log.error("读取委托列表失败: {error}", readError);
// 尝试创建文件
try {
// 创建默认的JSON结构
const defaultJson = JSON.stringify(
{
fight: [],
talk: [],
},
null,
2
);
const writeResult = file.writeTextSync(
SUPPORT_LIST_PATH,
defaultJson
);
if (writeResult) {
log.info("已创建空的委托列表文件");
} else {
log.error("创建委托列表文件失败");
}
} catch (writeError) {
log.error("创建委托列表文件失败: {error}", writeError);
}
}
} catch (error) {
log.error("处理委托列表时出错: {error}", error);
}
return supportedCommissions;
}
// 清理文本(去除标点符号等)
function cleanText(text) {
if (!text) return "";
// 去除标点符号和特殊字符
return text.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "").trim();
}
// 进入委托界面
async function enterCommissionScreen() {
log.info("正在进入委托界面...");
try {
// 使用F1快捷键直接打开委托界面
log.info("尝试使用F1快捷键打开委托界面");
keyPress("VK_F1");
// 点击委托界面
log.info("点击委托界面");
await sleep(900);
click(300, 350);
await sleep(100);
log.info("已进入委托界面");
return true;
} catch (error) {
log.error("进入委托界面失败: {error}", error);
return false;
}
}
// 自动执行划页操作 - 新的滑动方法
async function PageScroll(scrollCount) {
try {
const clickX = 950; // 假设点击的起始坐标
const clickY = 600;
const totalDistance = 300; // 假设每次滑动的总距离
const stepDistance = 10; // 每步移动的距离
for (let i = 0; i < scrollCount; ++i) {
log.info(`开始第 ${i + 1} 次滑动`);
// 如果点击坐标为 (0, 0),则跳过点击
if (clickX !== 0 || clickY !== 0) {
moveMouseTo(clickX, clickY); // 移动到指定坐标
await sleep(100);
}
// 按住鼠标左键
leftButtonDown();
// 将鼠标移动到目标位置,模拟更自然的拖动操作
const steps = totalDistance / stepDistance; // 分成若干步移动
for (let j = 0; j < steps; j++) {
moveMouseBy(0, -stepDistance); // 每次移动 stepDistance 像素
await sleep(10); // 每次移动后延迟10毫秒
}
// 释放鼠标左键
await sleep(700);
leftButtonUp();
await sleep(1000); // 增加滑动后的等待时间,确保界面稳定
}
return true;
} catch (error) {
log.error(`执行滑动操作时发生错误:${error.message}`);
return false;
}
}
// 识别委托地点
function recognizeCommissionLocation() {
try {
log.info(
"识别委托地点 ({x}, {y}) ({width}, {height})...",
LOCATION_OCR_X,
LOCATION_OCR_Y,
LOCATION_OCR_X + LOCATION_OCR_WIDTH,
LOCATION_OCR_Y + LOCATION_OCR_HEIGHT
);
// 创建OCR识别对象
const locationOcrRo = RecognitionObject.Ocr(
LOCATION_OCR_X,
LOCATION_OCR_Y,
LOCATION_OCR_WIDTH,
LOCATION_OCR_HEIGHT
);
// 截图识别
let captureRegion = captureGameRegion();
let results = captureRegion.findMulti(locationOcrRo);
if (results.count > 0) {
// 取第一个结果作为地点
return results[0].text.trim();
}
return "未知地点";
} catch (error) {
log.error("识别委托地点时出错: {error}", error);
return "识别失败";
}
}
// 保存委托数据到文件
function saveCommissionsData(commissionsTable) {
try {
log.info("保存委托数据到文件...");
// 创建JSON格式的委托数据
const commissionsData = {
timestamp: new Date().toISOString(),
commissions: commissionsTable,
};
// 保存到文件
const outputPath = `${OUTPUT_DIR}/commissions_data.json`;
try {
const jsonResult = file.writeTextSync(
outputPath,
JSON.stringify(commissionsData, null, 2)
);
if (jsonResult) {
log.info("委托数据已保存到: {path}", outputPath);
} else {
log.error("保存委托数据失败");
}
} catch (jsonError) {
log.error("保存委托数据失败: {error}", jsonError);
}
// 创建可读的文本报告
let reportContent = "# 原神每日委托识别报告\r\n";
reportContent += `生成时间: ${new Date().toLocaleString()}\r\n\r\n`;
reportContent += "## 委托列表\r\n\r\n";
for (const commission of commissionsTable) {
const supportStatus = commission.supported ? "✅ 支持" : "❌ 不支持";
const locationInfo = commission.location
? `(${commission.location})`
: "";
reportContent += `${commission.id}. ${commission.name} ${locationInfo} - ${supportStatus}\r\n`;
}
// 保存报告
const reportPath = `${OUTPUT_DIR}/commissions_report.txt`;
try {
const reportResult = file.writeTextSync(reportPath, reportContent);
if (reportResult) {
log.info("委托报告已保存到: {path}", reportPath);
} else {
log.error("保存委托报告失败");
}
} catch (reportError) {
log.error("保存委托报告失败: {error}", reportError);
}
return commissionsTable.filter((c) => c.supported);
} catch (error) {
log.error("处理委托数据时出错: {error}", error);
return [];
}
}
function ensureDirectoryExists(dirPath) {
try {
// 尝试创建目录如果目录已存在writeTextSync不会报错
// 创建一个临时文件来确保目录存在
const tempFilePath = `${dirPath}/.temp`;
file.writeTextSync(tempFilePath, "");
log.info(`已确保目录存在: ${dirPath}`);
return true;
} catch (error) {
log.error(`创建目录时出错: ${error}`);
return false;
}
}
// 检测是否进入委托详情界面
async function checkDetailPageEntered() {
try {
//log.info("检测是否进入委托详情界面...");
// 创建OCR识别对象 - 检测区域(1480,100)到(1535,130)
const detailOcrRo = RecognitionObject.Ocr(
1480,
100,
55, // 1535 - 1480
30 // 130 - 100
);
// 尝试3次OCR识别
for (let i = 0; i < 3; i++) {
//log.info(`执行第${i + 1}次详情界面OCR检测`);
let captureRegion = captureGameRegion();
let results = captureRegion.findMulti(detailOcrRo);
if (results.count > 0) {
// 检查OCR结果
for (let j = 0; j < results.count; j++) {
const text = results[j].text.trim();
//log.info(`检测到文本: "${text}"`);
// 如果有"蒙德",表示进入了详情界面
if (text.includes("蒙德")) {
log.info("检测到蒙德委托,成功进入详情界面");
return "蒙德";
}
// 如果没有文字,可能是已完成委托
else if (text === "") {
log.info("未检测到地区文本,可能是已完成委托");
return "已完成";
}
// 其他地区委托
else if (text.length >= 2) {
log.info(`检测到其他地区委托: ${text}`);
return text;
}
}
}
// 如果没有检测到,等待一会再试
await sleep(500);
}
log.info("三次OCR检测后仍未确认委托国家");
return "未知";
} catch (error) {
log.error("检测委托详情界面时出错: {error}", error);
return "错误";
}
}
/**
* 优化后的委托识别主函数
*/
async function Identification() {
try {
log.info("开始执行原神每日委托识别脚本");
await genshin.returnMainUi();
setGameMetrics(1920, 1080, 1);
// 加载支持的委托列表
const supportedCommissions = loadSupportedCommissions();
// 确保所有委托的资源目录存在
for (const commission of supportedCommissions.fight) {
ensureDirectoryExists(`assets/${commission}`);
}
for (const commission of supportedCommissions.talk) {
ensureDirectoryExists(`assets/process/${commission}`);
}
// 进入委托界面
const enterSuccess = await enterCommissionScreen();
if (!enterSuccess) {
log.error("无法进入委托界面,脚本终止");
return;
}
await sleep(1000);
// 步骤1: 执行第一次OCR识别
log.info("步骤1: 执行第一次OCR识别");
const ocrRo = RecognitionObject.Ocr(
OCR_REGION_X,
OCR_REGION_Y,
OCR_REGION_WIDTH,
OCR_REGION_HEIGHT
);
// 第一次截图识别
log.info(
"执行第一次OCR识别 ({x}, {y}) ({width}, {height})",
OCR_REGION_X,
OCR_REGION_Y,
OCR_REGION_X + OCR_REGION_WIDTH,
OCR_REGION_Y + OCR_REGION_HEIGHT
);
let captureRegion = captureGameRegion();
let firstResults = captureRegion.findMulti(ocrRo);
log.info("第一次OCR识别结果数量: {count}", firstResults.count);
// 处理第一次识别结果
let firstCommissions = [];
for (let i = 0; i < firstResults.count; i++) {
let result = firstResults[i];
let text = cleanText(result.text);
if (text && text.length >= minTextLength) {
log.info('第{index}个委托: "{text}"', i + 1, text);
// 检查委托类型
const isFightCommission = supportedCommissions.fight.includes(text);
const isTalkCommission = supportedCommissions.talk.includes(text);
const isSupported = isFightCommission || isTalkCommission;
const commissionType = isFightCommission
? COMMISSION_TYPE.FIGHT
: isTalkCommission
? COMMISSION_TYPE.TALK
: "";
firstCommissions.push({
id: i + 1,
name: text,
supported: isSupported,
type: commissionType,
location: "",
});
}
}
// 步骤2: 使用图像识别检测前3个委托的完成状态
log.info("步骤2: 检测前3个委托的完成状态");
for (let i = 0; i < Math.min(3, firstCommissions.length); i++) {
const commission = firstCommissions[i];
// 使用图像识别检测完成状态
const status = await detectCommissionStatusByImage(i);
if (status === "completed") {
log.info(
"委托{id} {name} 已完成,跳过详情查看",
commission.id,
commission.name
);
commission.location = "已完成";
continue;
} else if (status === "uncompleted") {
log.info(
"委托{id} {name} 未完成,查看详情",
commission.id,
commission.name
);
} else {
log.warn(
"委托{id} {name} 状态未知,尝试查看详情",
commission.id,
commission.name
);
}
// 只有未完成或状态未知的委托才点击查看详情
log.info(
"查看第{id}个委托详情: {name}",
commission.id,
commission.name
);
// 点击详情按钮
const detailButton = COMMISSION_DETAIL_BUTTONS[commission.id - 1];
log.info("点击委托详情按钮 ({x}, {y})", detailButton.x, detailButton.y);
click(detailButton.x, detailButton.y);
await sleep(2500);
// 检测委托国家
const detailStatus = await checkDetailPageEntered();
log.info(`委托国家: ${detailStatus}`);
commission.country = detailStatus;
const location = recognizeCommissionLocation();
commission.location = location;
log.info("委托 {name} 的地点: {location}", commission.name, location);
// 退出详情页面并获取地图坐标
if (commission.location !== "已完成") {
log.info("退出详情页面 - 按ESC");
keyDown("VK_ESCAPE");
await sleep(300);
keyUp("VK_ESCAPE");
await sleep(1200);
// 获取地图坐标并保存
const bigMapPosition = genshin.getPositionFromBigMap();
if (bigMapPosition) {
currentCommissionPosition = bigMapPosition;
commission.CommissionPosition = bigMapPosition;
log.info(
"当前委托位置: ({x}, {y})",
bigMapPosition.x,
bigMapPosition.y
);
}
keyDown("VK_ESCAPE");
await sleep(300);
keyUp("VK_ESCAPE");
await sleep(1200);
}
}
// 步骤3: 执行下滑操作
log.info("步骤3: 执行下滑操作");
await PageScroll(1);
await sleep(2000);
// 步骤4: 执行第二次OCR识别
log.info(
"步骤4: 执行第二次OCR识别({x}, {y}) ({width}, {height})",
OCR_REGION_X,
OCR_REGION_Y,
OCR_REGION_X + OCR_REGION_WIDTH,
OCR_REGION_Y + OCR_REGION_HEIGHT
);
captureRegion = captureGameRegion();
let secondResults = captureRegion.findMulti(ocrRo);
log.info("第二次OCR识别结果数量: {count}", secondResults.count);
// 处理第二次识别结果
let fourthCommission = null;
for (let i = 0; i < secondResults.count; i++) {
let result = secondResults[i];
let text = cleanText(result.text);
if (text && text.length >= minTextLength) {
log.info('第4个委托: "{text}"', text);
// 检查委托类型
const isFightCommission = supportedCommissions.fight.includes(text);
const isTalkCommission = supportedCommissions.talk.includes(text);
const isSupported = isFightCommission || isTalkCommission;
const commissionType = isFightCommission
? COMMISSION_TYPE.FIGHT
: isTalkCommission
? COMMISSION_TYPE.TALK
: "";
fourthCommission = {
id: 4,
name: text,
supported: isSupported,
type: commissionType,
location: "",
};
}
}
// 步骤5: 检测第4个委托的完成状态
if (fourthCommission) {
log.info("步骤5: 检测第4个委托的完成状态");
// 使用图像识别检测完成状态
const status = await detectCommissionStatusByImage(3); // 第4个按钮索引为3
if (status === "completed") {
log.info("委托4 {name} 已完成,跳过详情查看", fourthCommission.name);
fourthCommission.location = "已完成";
} else if (status === "uncompleted" || status === "unknown") {
log.info("委托4 {name} 需要查看详情", fourthCommission.name);
// 点击详情按钮
const detailButton = COMMISSION_DETAIL_BUTTONS[3];
log.info(
"点击委托详情按钮 ({x}, {y})",
detailButton.x,
detailButton.y
);
click(detailButton.x, detailButton.y);
await sleep(2500);
// 检测是否成功进入详情界面并获取委托国家
const detailStatus = await checkDetailPageEntered();
log.info(`委托国家: ${detailStatus}`);
fourthCommission.country = detailStatus;
// 根据检测结果处理
if (fourthCommission.location === "已完成") {
log.info("该委托已完成,跳过地点识别和退出操作");
fourthCommission.location = "已完成";
} else {
location = recognizeCommissionLocation();
fourthCommission.location = location;
log.info(
"委托 {name} 的地点: {location}",
fourthCommission.name,
location
);
}
// 退出详情页面并获取地图坐标
if (fourthCommission.location !== "未知") {
log.info("退出详情页面 - 按ESC");
keyDown("VK_ESCAPE");
await sleep(300);
keyUp("VK_ESCAPE");
await sleep(1200);
// 获取地图坐标并保存
const bigMapPosition = genshin.getPositionFromBigMap();
if (bigMapPosition) {
currentCommissionPosition = bigMapPosition;
fourthCommission.CommissionPosition = bigMapPosition;
log.info(
"当前委托位置: ({x}, {y})",
bigMapPosition.x,
bigMapPosition.y
);
}
keyDown("VK_ESCAPE");
await sleep(300);
keyUp("VK_ESCAPE");
await sleep(1200);
}
}
}
// 合并所有委托结果
let allCommissions = [...firstCommissions];
if (fourthCommission) {
allCommissions.push(fourthCommission);
}
// 输出完整委托列表
log.info("完整委托列表:");
for (const commission of allCommissions) {
const supportStatus = commission.supported ? "✅ 支持" : "❌ 不支持";
const locationInfo = commission.location
? `(${commission.location})`
: "";
const typeInfo = commission.type ? `[${commission.type}]` : "";
log.info(
"{id}. {name} {location} {type} - {status}",
commission.id,
commission.name,
locationInfo,
typeInfo,
supportStatus
);
}
// 保存委托数据
saveCommissionsData(allCommissions);
log.info("原神每日委托识别脚本执行完成");
} catch (error) {
log.error("脚本执行出错: {error}", error);
}
}
/**
* 计算两点之间的距离
* @param {Object} pos1 - 位置1 {x, y}
* @param {Object} pos2 - 位置2 {x, y}
* @returns {number} 距离
*/
function calculateDistance(point1, point2) {
if (
!point1 ||
!point2 ||
!point1.X ||
!point1.Y ||
!point2.x ||
!point2.y
) {
log.warn("无效的位置数据");
return Infinity;
}
return Math.sqrt(
Math.pow(point1.X - point2.x, 2) + Math.pow(point1.Y - point2.y, 2)
);
}
/**
* 获取委托的目标坐标(从路径追踪文件中获取最后一个坐标)
* @param {string} scriptPath - 委托的脚本路径
* @returns {Object|null} 目标坐标 {x, y} 或 null
*/
async function getCommissionTargetPosition(scriptPath) {
try {
const scriptContent = await file.readText(scriptPath);
const pathData = JSON.parse(scriptContent);
if (!pathData.positions || pathData.positions.length === 0) {
log.warn("路径追踪文件 {path} 中没有有效的坐标数据", scriptPath);
return null;
}
const lastPosition = pathData.positions[pathData.positions.length - 1];
if (!lastPosition.x || !lastPosition.y) {
log.warn(
"路径追踪文件 {path} 的最后一个路径点缺少坐标数据",
scriptPath
);
return null;
}
log.debug(
"从脚本路径 {path} 获取到目标坐标: ({x}, {y})",
scriptPath,
lastPosition.x,
lastPosition.y
);
return {
x: lastPosition.x,
y: lastPosition.y,
};
} catch (error) {
log.error("获取委托目标位置时出错: {error}", error);
return null;
}
}
/**
* 统一的对话委托流程处理器
* @param {Array} processSteps - 处理步骤数组
* @param {string} commissionName - 委托名称
* @param {string} location - 委托地点
* @returns {Promise<boolean>} - 执行结果
*/
async function executeUnifiedTalkProcess(
processSteps,
commissionName,
location
) {
try {
log.info("执行统一对话委托流程: {name}", commissionName);
// 加载优先选项和白名单NPC名称的默认值
let priorityOptions = [];
let npcWhiteList = [];
let clickedExtractedName = false;
// 定义识别对象
const paimonMenuRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync("Data/RecognitionObject/paimon_menu.png"),
0,
0,
genshin.width / 3.0,
genshin.width / 5.0
);
// 判断是否在主界面的函数
const isInMainUI = () => {
let captureRegion = captureGameRegion();
let res = captureRegion.Find(paimonMenuRo);
return !res.isEmpty();
};
// 人名提取函数
const extractName = (text) => {
const patterns = [
/与(.+?)对话/,
/与(.+?)一起/,
/同(.+?)交谈/,
/向(.+?)打听/,
/向(.+?)回报/,
/陪同(.+?)\S+/,
/找到(.+?)\S+/,
/询问(.+?)\S+/,
/拜访(.+?)\S+/,
/寻找(.+?)\S+/,
/告诉(.+?)\S+/,
/带(.+?)去\S+/,
/跟随(.+?)\S+/,
/协助(.+?)\S+/,
/请教(.+?)\S+/,
/拜托(.+?)\S+/,
/委托(.+?)\S+/,
];
for (const pattern of patterns) {
const match = text.match(pattern);
if (match && match[1]) {
return match[1].trim();
}
}
return null;
};
// 自动剧情函数
const executeOptimizedAutoTalk = async (
extractedName = null,
skipCount = 5,
customPriorityOptions = null,
customNpcWhiteList = null
) => {
// 使用传入的参数,不再加载默认配置
const effectivePriorityOptions = customPriorityOptions || [];
const effectiveNpcWhiteList = customNpcWhiteList || [];
// 初始化
keyPress("V");
// 初始触发剧情 - 识别人名并点击
extractedName = [];
let captureRegion = captureGameRegion();
let nameArea = captureRegion.DeriveCrop(75, 240, 225, 60); // 人名区域
let nameOcrRo = RecognitionObject.Ocr(
0,
0,
nameArea.width,
nameArea.height
);
let nameResults = nameArea.FindMulti(nameOcrRo);
// 尝试提取任务人名
for (let i = 0; i < nameResults.count; i++) {
let text = nameResults[i].text;
log.info(`任务区域识别文本: ${text}`);
// 尝试提取任务人名
let name = extractName(text);
if (name) {
extractedName = name;
log.info(`提取到人名: ${extractedName}`);
break;
}
}
nameArea = captureRegion.DeriveCrop(1150, 300, 350, 400);
//nameArea = captureRegion.DeriveCrop(0, 0, 1920, 1080);
nameOcrRo = RecognitionObject.Ocr(
0,
0,
nameArea.width,
nameArea.height
);
nameResults = nameArea.FindMulti(nameOcrRo);
let clickedWhitelistNPC = false;
// 处理人名区域的OCR结果
if (nameResults.count > 0) {
log.info(`人名区域识别到 ${nameResults.count} 个文本`);
// 首先尝试点击白名单中的NPC
for (let i = 0; i < nameResults.count; i++) {
let text = nameResults[i].text;
let res = nameResults[i];
log.info(
"人名区域识别到{text}:位置({x},{y},{h},{w})",
res.text,
res.x,
res.y,
res.width,
res.Height
);
// 检查是否包含白名单中的NPC名称
for (let j = 0; j < effectiveNpcWhiteList.length; j++) {
if (text.includes(effectiveNpcWhiteList[j])) {
log.info(
`找到白名单NPC: ${effectiveNpcWhiteList[j]}点击该NPC`
);
keyDown("VK_MENU");
await sleep(500);
click(res.x + 1150, res.y + 300);
leftButtonClick();
keyUp("VK_MENU");
clickedWhitelistNPC = true;
break;
}
}
if (clickedWhitelistNPC) break;
}
// 如果没有点击白名单NPC尝试点击包含提取到的人名的选项
if (!clickedWhitelistNPC && extractedName) {
for (let i = 0; i < nameResults.count; i++) {
let text = nameResults[i].text;
let res = nameResults[i];
if (text.includes(extractedName)) {
log.info(`点击包含提取到任务人名的选项: ${text}`);
keyDown("VK_MENU");
await sleep(500);
click(res.x + 1150, res.y + 300);
leftButtonClick();
keyUp("VK_MENU");
clickedExtractedName = true;
break;
}
}
}
}
// 如果没有找到NPC使用默认触发
if (!clickedWhitelistNPC && !clickedExtractedName) {
log.info("未找到匹配的NPC使用默认触发方式");
keyPress("F"); // 默认触发剧情
await sleep(500);
}
// 重复执行自动剧情,直到返回主界面
let maxAttempts = 100; // 设置最大尝试次数,防止无限循环
let attempts = 0;
await sleep(1000);
log.info("开始执行自动剧情");
while (!isInMainUI() && attempts < maxAttempts) {
attempts++;
// 正常跳过对话
await genshin.chooseTalkOption(
"纳西妲美貌举世无双",
skipCount,
false
);
if (isInMainUI()) {
log.info("检测到已返回主界面,结束循环");
break;
}
// 每skipCount次跳过后进行OCR识别
if (true) {
//log.info("执行OCR识别对话选项");
// 检查是否有匹配的优先选项
let foundPriorityOption = false;
// 获取对话区域截图并进行OCR识别
let captureRegion = captureGameRegion();
let dialogArea = captureRegion.DeriveCrop(1250, 450, 550, 400); // 对话选项区域 1250,450 到 1800,850
// 创建OCR识别对象并识别文本
let ocrRo = RecognitionObject.Ocr(
0,
0,
dialogArea.width,
dialogArea.height
);
let ocrResults = dialogArea.FindMulti(ocrRo);
if (ocrResults.count > 0) {
log.info(`识别到 ${ocrResults.count} 个选项`);
for (let i = 0; i < ocrResults.count; i++) {
let ocrText = ocrResults[i].text;
// 检查是否在优先选项列表中
for (let j = 0; j < effectivePriorityOptions.length; j++) {
if (ocrText.includes(effectivePriorityOptions[j])) {
log.info(
`找到优先选项: ${effectivePriorityOptions[j]},点击该选项`
);
// 点击该选项
ocrResults[i].click();
await sleep(500);
foundPriorityOption = true;
break;
}
}
if (foundPriorityOption) break;
}
// 如果没有找到优先选项,则使用默认跳过
if (!foundPriorityOption) {
await genshin.chooseTalkOption("", 1, false);
}
}
}
// 检查是否已返回主界面
if (isInMainUI()) {
log.info("检测到已返回主界面,结束循环");
break;
}
}
if (isInMainUI()) {
log.info("已返回主界面,自动剧情执行完成");
keyPress("V");
} else {
log.warn(`已达到最大尝试次数 ${maxAttempts},但未检测到返回主界面`);
}
};
// 执行处理步骤
for (let i = 0; i < processSteps.length; i++) {
const step = processSteps[i];
log.info("执行流程步骤 {step}: {type}", i + 1, step.type || step);
// 重置为默认值
priorityOptions = [];
npcWhiteList = [];
// 如果步骤中包含自定义的优先选项和NPC白名单则使用它们
if (step.data && typeof step.data === "object") {
if (Array.isArray(step.data.priorityOptions)) {
priorityOptions = step.data.priorityOptions;
log.info(
"使用自定义优先选项: {options}",
priorityOptions.join(", ")
);
}
if (Array.isArray(step.data.npcWhiteList)) {
npcWhiteList = step.data.npcWhiteList;
log.info("使用自定义NPC白名单: {npcs}", npcWhiteList.join(", "));
}
}
if (typeof step === "string") {//typeof step === "string"
// 简单格式处理
if (step.endsWith(".json")) {
// 地图追踪文件
const trackingPath = `${TALK_PROCESS_BASE_PATH}/${commissionName}/${location}/${step}`;
log.info("执行地图追踪: {path}", trackingPath);
try {
await pathingScript.runFile(trackingPath);
log.info("地图追踪执行完成");
} catch (error) {
log.error("执行地图追踪时出错: {error}", error);
}
} else if (step === "F") {
// 按F键并执行优化的自动剧情
log.info("执行自动剧情");
await executeOptimizedAutoTalk(
null,
5,
priorityOptions,
npcWhiteList
);
}
} else if (typeof step === "object") {
// JSON格式处理
if (step.note) {
log.info("步骤说明: {note}", step.note);
}
switch (step.type) {
case "地图追踪":
log.info("执行地图追踪: {path}", step.data);
try {
const fullPath = `${TALK_PROCESS_BASE_PATH}/${commissionName}/${location}/${step.data}`;
await pathingScript.runFile(fullPath);
log.info("地图追踪执行完成");
} catch (error) {
log.error("执行地图追踪时出错: {error}", error);
}
break;
case "追踪委托":
try {
// 获取目标NPC名称和图标类型
let targetNpc = "";
let iconType = "bigmap";
if (typeof step.data === "string") {
targetNpc = step.data;
} else if (typeof step.data === "object") {
if (step.data.npc) targetNpc = step.data.npc;
if (step.data.iconType) iconType = step.data.iconType;
}
log.info("执行追踪委托目标NPC: {target},图标类型: {type}", targetNpc, iconType);
await autoNavigateToTalk(targetNpc, iconType);
log.info("追踪委托执行完成");
} catch (error) {
log.error("执行追踪委托时出错: {error}", error.message);
}
break;
case "键鼠脚本":
log.info("执行键鼠脚本: {path}", step.data);
try {
const fullPath = `${TALK_PROCESS_BASE_PATH}/${commissionName}/${location}/${step.data}`;
await keyMouseScript.runFile(fullPath);
log.info("键鼠脚本执行完成");
} catch (error) {
log.error("执行键鼠脚本时出错: {error}", error.message);
}
break;
case "对话":
log.info("执行对话");
let skipCount = 5; // 默认跳过5次
// 处理对话选项
if (typeof step.data === "number") {
// 兼容旧版本如果data是数字则视为skipCount
skipCount = step.data;
} else if (typeof step.data === "object" && step.data.skipCount) {
// 新版本data是对象包含skipCount
skipCount = step.data.skipCount;
}
// 执行对话使用当前步骤的优先选项和NPC白名单
await executeOptimizedAutoTalk(
null,
skipCount,
priorityOptions,
npcWhiteList
);
break;
case "按键":
if (typeof step.data === "string") {
log.info("执行按键: {key}", step.data);
keyPress(step.data);
} else if (typeof step.data === "object") {
if (step.data.action === "down") {
log.info("按下按键: {key}", step.data.key);
keyDown(step.data.key);
} else if (step.data.action === "up") {
log.info("释放按键: {key}", step.data.key);
keyUp(step.data.key);
} else if (step.data.action === "press") {
log.info("点击按键: {key}", step.data.key);
keyPress(step.data.key);
}
}
break;
case "tp":
if (Array.isArray(step.data) && step.data.length >= 2) {
log.info("执行传送: {x}, {y}", step.data[0], step.data[1]);
const force = step.data.length > 2 ? step.data[2] : false;
await genshin.tp(step.data[0], step.data[1], force);
log.info("传送完成");
} else {
log.error("传送参数格式错误");
}
break;
case "isinMainUi":
for (let i = 0; i < 60; i++) {
if (isInMainUI()) {
log.info("检测到已返回主界面,结束等待");
break;
}
await sleep(1000);
}
if (!isInMainUI()) {
log.info("等待返回主界面超时,尝试继续执行后续步骤");
}
break;
default:
log.warn("未知的流程类型: {type}", step.type);
}
}
// 每个步骤之间等待一段时间
await sleep(2000);
}
log.info("统一对话委托流程执行完成: {name}", commissionName);
return true;
} catch (error) {
log.error("执行统一对话委托流程时出错: {error}", error.message);
return false;
}
}
/**
* 执行对话委托流程(优化版)
* @param {string} commissionName - 委托名称
* @param {string} location - 委托地点
* @returns {Promise<boolean>} - 执行结果
*/
async function executeTalkCommission(commissionName, location) {
try {
log.info(
"开始执行对话委托: {name} ({location})",
commissionName,
location
);
// 优化的文件路径:固定使用 ${processBasePath}/${location}/process.json
const processBasePath = TALK_PROCESS_BASE_PATH;
const processFilePath = `${processBasePath}/${commissionName}/${location}/process.json`;
let processContent;
try {
processContent = await file.readText(processFilePath);
log.info("找到对话委托流程文件: {path}", processFilePath);
} catch (error) {
log.warn(
"未找到对话委托 {name} 在 {location} 的流程文件: {path}",
commissionName,
location,
processFilePath
);
return false;
}
// 解析流程内容
let processSteps;
try {
// 尝试解析为JSON格式
const jsonData = JSON.parse(processContent);
if (Array.isArray(jsonData)) {
processSteps = jsonData;
} else {
log.error("JSON流程格式错误应为数组");
return false;
}
} catch (jsonError) {
// 如果不是JSON格式按简单格式处理
const lines = processContent
.split("\n")
.map((line) => line.trim())
.filter((line) => line.length > 0);
processSteps = lines;
}
// 使用统一的处理器执行流程
return await executeUnifiedTalkProcess(
processSteps,
commissionName,
location
);
} catch (error) {
log.error("执行对话委托时出错: {error}", error);
return false;
}
}
/**
* 执行对话委托流程
* @param {string} processPath - 流程文件路径
*/
async function executeTalkCommissionWithBranches(processPath) {
try {
log.info("开始执行对话委托流程: {path}", processPath);
// 读取流程文件
const processContent = await file.readText(processPath);
// 解析流程内容
const branches = parseProcessBranches(processContent);
// 确定要执行的分支
const branchToExecute = await determineBranch(branches);
if (branchToExecute) {
log.info("执行分支: {id}", branchToExecute.id);
await executeUnifiedTalkProcess(branchToExecute.steps);
} else {
log.warn("没有找到匹配的分支,执行默认流程");
// 尝试解析整个内容作为单一流程
const steps = JSON.parse(processContent);
await executeUnifiedTalkProcess(steps);
}
} catch (error) {
log.error("执行对话委托流程出错: {error}", error);
}
}
/**
* 解析流程文件中的分支
* @param {string} content - 流程文件内容
* @returns {Array} - 分支数组
*/
function parseProcessBranches(content) {
const branches = [];
const branchRegex = /分支:(\d+)[\s\S]*?判断方法"([^"]+)"[\s\S]*?data:"([^"]+)"([\s\S]*?)(?=分支:|$)/g;
let match;
while ((match = branchRegex.exec(content)) !== null) {
const branchId = parseInt(match[1]);
const judgmentMethod = match[2];
const judgmentData = match[3];
const stepsContent = match[4].trim();
// 解析步骤
let steps = [];
try {
// 尝试解析JSON数组
const jsonContent = `[${stepsContent}]`;
steps = JSON.parse(jsonContent);
} catch (error) {
log.warn("解析分支{id}的步骤出错: {error}", branchId, error);
continue;
}
branches.push({
id: branchId,
method: judgmentMethod,
data: judgmentData,
steps: steps
});
}
return branches;
}
/**
* 确定要执行的分支
* @param {Array} branches - 分支数组
* @returns {Object|null} - 要执行的分支或null
*/
async function determineBranch(branches) {
for (const branch of branches) {
switch (branch.method) {
case "坐标":
if (await checkCoordinateMatch(branch.data)) {
return branch;
}
break;
case "任务追踪":
if (await checkTaskMatch(branch.data)) {
return branch;
}
break;
default:
log.warn("未知的判断方法: {method}", branch.method);
}
}
return null;
}
/**
* 检查当前坐标是否匹配
* @param {string} coordData - 坐标数据 "x,y"
* @returns {Promise<boolean>} - 是否匹配
*/
async function checkCoordinateMatch(coordData) {
try {
const [targetX, targetY] = coordData.split(",").map(c => parseFloat(c.trim()));
// 获取当前委托位置
const playerPos = await getCurrentCommissionPosition();//委托位置会变,你每完成一个委托,未完成的委托就上一个
if (!playerPos) return false;
// 计算距离
const distance = calculateDistance(playerPos, { x: targetX, y: targetY });
log.info("当前位置: ({x}, {y}),目标位置: ({tx}, {ty}),距离: {d}",
playerPos.x, playerPos.y, targetX, targetY, distance);
// 如果距离小于阈值,认为匹配
return distance < 100; // 可以调整阈值
} catch (error) {
log.error("检查坐标匹配出错: {error}", error);
return false;
}
}
/**
* 检查当前任务是否匹配
* @param {string} taskName - 任务名称
* @returns {Promise<boolean>} - 是否匹配
*/
async function checkTaskMatch(taskName) {
try {
// 截取左上角任务区域
const captureRegion = captureGameRegion();
const taskArea = captureRegion.DeriveCrop(75, 240, 225, 60); // 任务区域
// 创建OCR识别对象
const taskOcrRo = RecognitionObject.Ocr(
0,
0,
taskArea.width,
taskArea.height
);
// 识别任务文本
const taskResults = taskArea.FindMulti(taskOcrRo);
// 检查是否包含目标任务名称
for (let i = 0; i < taskResults.count; i++) {
const text = taskResults[i].text;
log.info(`任务区域识别文本: ${text}`);
if (text.includes(taskName)) {
log.info(`找到匹配任务: ${taskName}`);
return true;
}
}
log.info(`未找到匹配任务: ${taskName}`);
return false;
} catch (error) {
log.error("检查任务匹配出错: {error}", error);
return false;
}
}
/**
* 检查委托状态
* @param completedCount — 已完成的委托数量
* @returns — 返回 ture,false
*/
async function iscompleted(completedCount) {
// 记录已完成的委托数量
log.info("已完成委托数量: {completedCount}", completedCount);
const enterSuccess = await enterCommissionScreen();
if (!enterSuccess) {
log.error("无法进入委托界面,脚本终止");
return false;
}
await sleep(900);
if (completedCount===0) {
await PageScroll(1);
const status = await detectCommissionStatusByImage(3);
if (status === "completed") {
return true;
} else {
return false;
}
}else{
const status = await detectCommissionStatusByImage(3-completedCount);
if (status === "completed") {
return true;
} else {
return false;
}
}
}
/**
* 执行委托追踪(优化版 - 按距离排序)
*/
async function executeCommissionTracking() {
try {
log.info("开始执行委托追踪 - 按距离排序模式");
dispatcher.addTimer(
new RealtimeTimer("AutoPick", { forceInteraction: false })
);
// 获取已识别的委托列表
let commissions = [];
try {
const commissionsData = JSON.parse(
file.readTextSync(`${OUTPUT_DIR}/commissions_data.json`)
);
commissions = commissionsData.commissions.filter((c) => c.supported);
log.info("已加载支持的委托数据,共 {count} 个", commissions.length);
} catch (error) {
log.error("读取委托数据失败: {error}", error);
return false;
}
if (commissions.length === 0) {
log.warn("没有找到支持的委托,请先运行识别脚本");
return false;
}
// 确保回到主界面
await genshin.returnMainUi();
// 统计已完成委托
let completedCount = 0;
for (const commission of commissions) {
if (commission.location === "已完成") {
completedCount++;
continue;
}
}
// 执行每个委托
for (const commission of commissions) {
// 跳过已完成的委托
if (commission.location === "已完成") {
log.info("委托 {name} 已完成,跳过", commission.name);
continue;
}
// 跳过没有地点信息的委托
if (
!commission.location ||
commission.location === "未知地点" ||
commission.location === "识别失败"
) {
log.warn("委托 {name} 缺少地点信息,跳过", commission.name);
continue;
}
log.info(
"开始执行委托: {name} ({location}) [{type}]",
commission.name,
commission.location,
commission.type || "未知类型"
);
try {
log.info(
"当前委托位置: ({x}, {y})",
commission.CommissionPosition.X,
commission.CommissionPosition.Y
);
} catch (error) {
log.warn("委托 {name} 缺少坐标信息,尝试全部执行", commission.name);
}
let success = false;
// 根据委托类型执行不同的处理逻辑
if (commission.type === COMMISSION_TYPE.TALK) {
// 执行对话委托
success = await executeTalkCommission(
commission.name,
commission.location
);
let completed = await iscompleted(completedCount);
if (success && completed) {
completedCount++;
log.info("对话委托 {name} 执行完成", commission.name);
} else {
log.warn("对话委托 {name} 执行失败", commission.name);
}
} else {
//dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false }));
// 默认执行战斗委托
const location = commission.location.trim();
// 脚本路径
const scriptPaths = [
`assets/${commission.name}/${location}-1.json`,
`assets/${commission.name}/${location}-2.json`,
`assets/${commission.name}/${location}-3.json`,
];
// 获取每个脚本对应的目标位置和距离
const scriptInfo = [];
for (const scriptPath of scriptPaths) {
try {
await file.readText(scriptPath);
const targetPos = await getCommissionTargetPosition(scriptPath);
if (targetPos) {
const distance = calculateDistance(
commission.CommissionPosition,
targetPos
);
scriptInfo.push({
path: scriptPath,
distance: distance,
valid: true,
});
log.info(
"委托 {name} 目标位置: ({x}, {y}),距离: {distance}",
scriptPath,
targetPos.x,
targetPos.y,
distance
);
} else {
log.warn("委托 {name} 无法获取距离", scriptPath);
scriptInfo.push({
path: scriptPath,
distance: Infinity,
valid: false,
});
}
} catch (readError) {
log.info("路径追踪脚本不存在: {path}", scriptPath);
continue;
}
}
// 按距离从小到大排序
scriptInfo.sort((a, b) => a.distance - b.distance);
// 输出排序结果
log.info("排序后的脚本执行顺序:");
scriptInfo.forEach((info, index) => {
log.info(
"{index}. 脚本: {path}, 距离: {distance}",
index + 1,
info.path,
info.distance
);
});
// 尝试执行排序后的脚本路径
let executed = false;
for (const info of scriptInfo) {
const scriptPath = info.path;
try {
/* 检查脚本文件是否存在
log.info("检查路径追踪脚本: {path}", scriptPath);
try {
// 尝试读取文件内容来检查是否存在
await file.readText(scriptPath);
log.info("找到路径追踪脚本: {path}", scriptPath);
scriptFound = true;
} catch (readError) {
log.info("路径追踪脚本不存在: {path}", scriptPath);
continue; // 尝试下一个脚本路径
}*/
// 执行路径追踪脚本
log.info("开始执行路径追踪脚本: {path}", scriptPath);
await pathingScript.runFile(scriptPath);
log.info("路径追踪脚本执行完成");
if (await iscompleted(completedCount)) {
log.info("委托 {name} 已完成", commission.name);
completedCount++;
success = true;
break;
} else {
log.info("委托 {name} 未完成,尝试下一个脚本", commission.name);
}
} catch (scriptError) {
log.error("执行路径追踪脚本时出错: {error}", scriptError);
break;
}
}
if (!success) {
log.warn("委托 {name} 执行失败", commission.name);
}
}
// 每个委托之间等待一段时间
log.info("等待5秒后执行下一个委托...");
await sleep(5000);
}
log.info(
"委托追踪全部执行完成,共执行 {count}/{total} 个委托",
completedCount,
commissions.length
);
return completedCount > 0;
} catch (error) {
log.error("执行委托追踪时出错: {error}", error.message);
return false;
}
}
// 主函数
async function main() {
//await Identification();
//setGameMetrics(1920, 1080, 1);
//await genshin.returnMainUi();
//await autoNavigateToTalk("盖伊","task");
if (skipRecognition) {
log.info("跳过识别,直接加载数据");
} else {
await Identification();
} //识别委托
// 开局准备
await prepareForLeyLineRun();`
// 执行自动委托
await executeCommissionTracking();
log.info("每日委托执行完成,前往安全地点");
await genshin.tpToStatueOfTheSeven();
}
// 使用 Promise 包装 main 函数的执行
return main();
//log.info("");
})();