Files
bettergi-scripts-list/repo/js/AutoCommission/lib/execute.js
DarkFlameMaster 44b608ccb0 同步AutoCommission更新 (#2600)
* 同步AutoCommission更新

* Update version to 0.98.14 in manifest.json

* Refine update instructions in checkVersion.js

Update log message to provide clearer instructions for users after an update.

---------

Co-authored-by: DarkFlameMaster <actions@github.com>
2025-12-31 16:01:06 +08:00

659 lines
20 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.
// 原神每日委托自动执行脚本 - 执行功能模块
var Execute = {
// 寻找委托目的地址带追踪任务
findCommissionTarget: async function(commissionName) {
try {
log.info("开始寻找委托目标位置: {name}", commissionName);
// 确保回到主界面
await genshin.returnMainUi();
// 第一步检测这个任务是否在1-3之中
var index = 4;
try {
// 进入委托界面
var enterSuccess = await UI.enterCommissionScreen();
if (!enterSuccess) {
log.error("无法进入委托界面");
return null;
}
await sleep(1000);
// 识别前3个委托
log.debug("findCommissionTarget识别前3个委托");
// 先识别前3个Main_Dev区域索引0-2
for (var regionIndex = 0; regionIndex < 3; regionIndex++) {
var region = Constants.OCR_REGIONS.Main_Dev[regionIndex];
try {
var results = await Utils.easyOCR(region);
// 处理识别结果,取第一个有效结果
for (var i = 0; i < results.count; i++) {
var result = results[i];
var text = Utils.cleanText(result.text);
if (text && text.length >= 2) {
log.info(
'第{regionIndex}个委托: "{text}"',
regionIndex + 1,
text
);
if (text === commissionName) {
index = regionIndex + 1;
log.info(
"找到委托 {name} 在位置 {index}",
commissionName,
index
);
break;
}
}
}
// 如果找到了委托,跳出外层循环
if (index !== 4) {
break;
}
} catch (regionError) {
log.error(
"识别第{index}个委托区域时出错: {error}",
regionIndex + 1,
regionError
);
continue;
}
}
} catch (error) {
log.error("findCommissionTarget第一步失败: {error}", error.message);
}
// 如果前3个没找到检查第4个委托需要翻页
if (index === 4) {
try {
log.info("前3个委托中未找到检查第4个委托");
await UI.pageScroll(1);
var region = Constants.OCR_REGIONS.Main_Dev[3]; // 第4个区域
var results = await Utils.easyOCR(region);
for (var i = 0; i < results.count; i++) {
var result = results[i];
var text = Utils.cleanText(result.text);
if (text && text.length >= 2) {
log.info('第4个委托: "{text}"', text);
if (text === commissionName) {
index = 4;
log.info("找到委托 {name} 在第4个位置", commissionName);
break;
}
}
}
} catch (fourthError) {
log.error("识别第4个委托时出错: {error}", fourthError);
}
}
// 第二步:进入对应的大地图,获取位置
var currentCommissionPosition = null;
try {
// 点击详情按钮
if (index === 4) {
// 第4个委托已经翻页了使用索引3
index = 3;
}
var button = Constants.COMMISSION_DETAIL_BUTTONS[index - 1];
if (button) {
log.info("点击委托详情按钮: {id}", button.id);
click(button.x, button.y);
await sleep(2000);
// 检查是否有追踪按钮并点击
var trackingResult = await Utils.easyOCROne(
Constants.OCR_REGIONS.COMMISSION_TRACKING
);
if (trackingResult === "追踪") {
log.info("发现追踪按钮,点击追踪");
click(1693, 1000);
await sleep(1000);
}
// 退出详情页面
log.info("退出详情页面 - 按ESC");
keyDown("VK_ESCAPE");
await sleep(300);
keyUp("VK_ESCAPE");
await sleep(1200);
let scale = 2.0
let bigMapPosition
while (scale <= 5.0) {
try {
await genshin.setBigMapZoomLevel(scale);
bigMapPosition = genshin.getPositionFromBigMap();
break;
} catch {
scale += 0.1;
}
await sleep(100);
}
if (bigMapPosition) {
currentCommissionPosition = bigMapPosition;
log.info(
"当前委托位置: ({x}, {y})",
bigMapPosition.x,
bigMapPosition.y
);
}
await genshin.returnMainUi();
} else {
log.error("无效的委托按钮索引: {index}", index);
}
} catch (error) {
log.error("findCommissionTarget第2步失败: {error}", error.message);
}
return currentCommissionPosition;
} catch (error) {
log.error("寻找委托目标位置时出错: {error}", error.message);
return null;
}
},
// 读取并解析流程文件为步骤数组
loadAndParseProcessFile: async function(
commissionName,
location,
locationprocessFilePath
) {
locationprocessFilePath = locationprocessFilePath || "process.json";
var processFilePath = Constants.TALK_PROCESS_BASE_PATH + "/" + commissionName + "/" + location + "/" + locationprocessFilePath;
var processContent;
var processSteps;
try {
processContent = await file.readText(processFilePath);
log.info("找到对话委托流程文件: {path}", processFilePath);
} catch (error) {
log.warn(
"未找到对话委托 {name} 在 {location} 的流程文件: {path}",
commissionName,
location,
processFilePath
);
return false;
}
// 解析流程内容
try {
// 尝试解析为JSON格式
var jsonData = JSON.parse(processContent);
if (Array.isArray(jsonData)) {
processSteps = jsonData;
log.debug("JSON流程解析成功");
} else {
log.error("JSON流程格式错误应为数组");
return false;
}
} catch (jsonError) {
// 如果不是JSON格式按简单格式处理
var lines = processContent
.split("\n")
.map(function(line) { return line.trim(); })
.filter(function(line) { return line.length > 0; });
processSteps = lines;
}
return processSteps;
},
// 执行对话委托流程(优化版)
executeTalkCommission: async function(commissionName, location) {
try {
var processSteps = await Execute.loadAndParseProcessFile(
commissionName,
location,
"process.json"
);
// 使用统一的处理器执行流程
return await Execute.executeUnifiedTalkProcess(
processSteps,
commissionName,
location
);
} catch (error) {
log.error("执行对话委托时出错: {error}", error.message);
return false;
}
},
// 自动导航到NPC对话位置从main_branch.js移植
autoNavigateToTalk: async function(npcName, iconType,autoTalk) {
npcName = npcName || "";
iconType = iconType || "";
autoTalk = autoTalk || false;
try {
// 设置目标NPC名称
var textArray = npcName;
// 根据图标类型选择不同的识别对象
var boxIconRo;
if (iconType === "Bigmap") {
boxIconRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync(
"Data/RecognitionObject/IconBigmapCommission.jpg"
)
);
log.info("使用大地图图标");
} else if (iconType === "Question") {
boxIconRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync(
"Data/RecognitionObject/IconQuestionCommission.png"
)
);
log.info("使用问号任务图标");
} else {
// 默认使用任务图标
boxIconRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync(
"Data/RecognitionObject/IconTaskCommission.png"
)
);
log.info("使用任务图标");
}
var advanceNum = 0; //前进次数
middleButtonClick();
await sleep(800);
while (true) {
// 1. 优先检查是否已到达
await sleep(500); // 等待0.5秒
var captureRegion = captureGameRegion();
var rewardTextArea = captureRegion.DeriveCrop(1210, 515, 200, 50);
var rewardResult = rewardTextArea.find(RecognitionObject.ocrThis);
captureRegion.dispose();
rewardTextArea.dispose();
log.debug("检测到文字: " + rewardResult.text);
// 检测到特点文字则结束!!!
if (rewardResult.text == textArray) {
log.info("已到达指定位置,检测到文字: " + rewardResult.text);
if (autoTalk) {
keyPress("VK_F");
}
return;
} else if (advanceNum > 80) {
throw new Error("前进时间超时");
}
// 2. 未到达领奖点,则调整视野
for (var i = 0; i < 100; i++) {
captureRegion = captureGameRegion();
var iconRes = captureRegion.Find(boxIconRo);
captureRegion.dispose();
log.info("检测到委托图标位置 ({x}, {y})", iconRes.x, iconRes.y);
if (iconRes.x >= 920 && iconRes.x <= 980 && iconRes.y <= 540) {
advanceNum++;
log.info("视野已调正,前进第{num}次", advanceNum);
break;
} else {
// 小幅度调整
if (iconRes.y >= 520) moveMouseBy(0, 920);
var adjustAmount = iconRes.x < 920 ? -20 : 20;
var distanceToCenter = Math.abs(iconRes.x - 920); // 计算与920的距离
var scaleFactor = Math.max(1, Math.floor(distanceToCenter / 50)); // 根据距离缩放最小为1
var adjustAmount2 = iconRes.y < 540 ? scaleFactor : 10;
moveMouseBy(adjustAmount * adjustAmount2, 0);
await sleep(100);
}
if (i > 50) throw new Error("视野调整超时");
}
// 3. 前进一小步
keyDown("w");
await sleep(200);
keyPress("VK_SPACE");
await sleep(200);
keyPress("VK_SPACE");
await sleep(200);
keyUp("w");
await sleep(200); // 等待角色移动稳定
}
} catch (error) {
log.error("自动导航到NPC对话位置时出错: {error}", error.message);
throw error;
}
},
// 统一的对话委托流程处理器(重构版 - 更简洁的主控制函数)
executeUnifiedTalkProcess: async function(
processSteps,
commissionName,
location
) {
try {
log.info("执行统一对话委托流程: {name}", commissionName);
if (!processSteps || processSteps.length === 0) {
log.warn("没有找到有效的流程步骤");
return false;
}
// 初始化UI检测器和配置
var isInMainUI = UI.UIUtils.isInMainUI;
var priorityOptions = [];
var npcWhiteList = [];
// 刚开始就追踪委托目标
await Execute.findCommissionTarget(commissionName);
// 执行处理步骤
for (var i = 0; i < processSteps.length; i++) {
var step = processSteps[i];
log.info("执行流程步骤 {step}: {type}", i + 1, step.type || step);
try {
// 重置为默认值并处理自定义配置
var stepConfig = Execute.processStepConfiguration(
step,
priorityOptions,
npcWhiteList
);
priorityOptions = stepConfig.priorityOptions;
npcWhiteList = stepConfig.npcWhiteList;
var context = {
commissionName: commissionName,
location: location,
processSteps: processSteps,
currentIndex: i,
isInMainUI: isInMainUI,
priorityOptions: priorityOptions,
npcWhiteList: npcWhiteList,
};
// 处理步骤
await Execute.processStep(step, context);
} catch (stepError) {
log.error(
"执行步骤 {step} 时出错: {error}",
i + 1,
stepError.message
);
// 继续执行下一步,不中断整个流程
}
// 每个步骤之间等待一段时间
await sleep(2000);
}
log.info("统一对话委托流程执行完成: {name}", commissionName);
return true;
} catch (error) {
log.error("执行统一对话委托流程时出错: {error}", error.message);
return false;
}
},
// 处理步骤配置优先选项和NPC白名单
processStepConfiguration: function(
step,
defaultPriorityOptions,
defaultNpcWhiteList
) {
var priorityOptions = defaultPriorityOptions.slice(); // 复制数组
var npcWhiteList = defaultNpcWhiteList.slice(); // 复制数组
// 如果步骤中包含自定义的优先选项和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(", "));
}
}
return { priorityOptions: priorityOptions, npcWhiteList: npcWhiteList };
},
// 处理单个步骤
processStep: async function(step, context) {
if (typeof step === "string") {
// 简单格式处理
await Execute.processStringStep(step, context);
} else if (typeof step === "object") {
// JSON格式处理
await Execute.processObjectStep(step, context);
}
},
// 处理字符串格式的步骤
processStringStep: async function(step, context) {
if (step.endsWith(".json")) {
// 地图追踪文件
await StepProcessorLoader.process({
type: "地图追踪",
data: step
}, context);
} else if (step === "F") {
// 按F键并执行优化的自动剧情
log.info("执行自动剧情");
await DialogProcessor.executeOptimizedAutoTalk(
null,
5,
context.priorityOptions,
context.npcWhiteList,
context.isInMainUI
);
}
},
// 处理对象格式的步骤
processObjectStep: async function(step, context) {
if (step.note) {
log.info("步骤说明: {note}", step.note);
}
// 使用步骤处理器加载器来处理步骤
await StepProcessorLoader.process(step, context);
},
// 处理对话步骤
processDialogStep: async function(
step,
priorityOptions,
npcWhiteList,
isInMainUI
) {
log.info("执行对话");
var skipCount = 2; // 默认跳过2次
// 处理对话选项
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 DialogProcessor.executeOptimizedAutoTalk(
null,
skipCount,
priorityOptions,
npcWhiteList,
isInMainUI
);
},
// 按类型执行委托列表
executeCommissionsByType: async function(commissions, type) {
var results = [];
for (var i = 0; i < commissions.length; i++) {
var commission = commissions[i];
try {
log.info(
"执行第 {current}/{total} 个{type}委托: {name} ({location})",
i + 1,
commissions.length,
type === "talk" ? "对话" : "战斗",
commission.name,
commission.location
);
var success = false;
if (type === "talk") {
success = await Execute.executeTalkCommission(
commission.name,
commission.location
);
} else if (type === "fight") {
success = await Execute.executeFightCommission(commission);
}
results.push({
commission: commission,
success: success,
type: type,
});
if (success) {
log.info("委托 {name} 执行成功", commission.name);
} else {
log.warn("委托 {name} 执行失败", commission.name);
}
await sleep(2000);
} catch (commissionError) {
log.error(
"执行委托 {name} 时出错: {error}",
commission.name,
commissionError.message
);
results.push({
commission: commission,
success: false,
type: type,
error: commissionError.message,
});
}
}
return results;
},
// 执行战斗委托
executeFightCommission: async function(commission) {
try {
log.info("执行战斗委托: {name}", commission.name);
var location = commission.location.trim();
// 脚本路径
var scriptPaths = [
"assets/" + commission.name + "/" + location + "-1.json",
"assets/" + commission.name + "/" + location + "-2.json",
"assets/" + commission.name + "/" + location + "-3.json",
];
// 获取每个脚本对应的目标位置和距离
var scriptInfo = [];
for (var i = 0; i < scriptPaths.length; i++) {
var scriptPath = scriptPaths[i];
try {
file.readTextSync(scriptPath);
var targetPos = await CommissionBasic.getCommissionTargetPosition(scriptPath);
if (targetPos) {
var distance = Utils.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(function(a, b) { return a.distance - b.distance; });
// 输出排序结果
log.info("排序后的脚本执行顺序:");
scriptInfo.forEach(function(info, index) {
log.info(
"{index}. 脚本: {path}, 距离: {distance}",
index + 1,
info.path,
info.distance
);
});
// 尝试执行排序后的脚本路径
var scriptSuccess = false;
for (var j = 0; j < scriptInfo.length; j++) {
var info = scriptInfo[j];
var scriptPath = info.path;
try {
// 执行路径追踪脚本
log.info("开始执行路径追踪脚本: {path}", scriptPath);
dispatcher.addTimer(
new RealtimeTimer("AutoPick", { forceInteraction: false })
);
// dispatcher.addTimer(new RealtimeTimer("AutoEat"));
await pathingScript.runFile(scriptPath);
log.info("路径追踪脚本执行完成");
dispatcher.ClearAllTriggers();
scriptSuccess = true;
break;
} catch (scriptError) {
log.error("执行路径追踪脚本时出错: {error}", scriptError);
continue; // 尝试下一个脚本
}
}
if (scriptSuccess) {
log.info("战斗委托 {name} 执行完成", commission.name);
return true;
} else {
log.warn("战斗委托 {name} 所有脚本执行失败", commission.name);
return false;
}
} catch (error) {
log.error("执行战斗委托时出错: {error}", error.message);
return false;
}
},
};