Files
bettergi-scripts-list/repo/js/铁匠铺/main.js
呱呱z 37a6de176e 铁匠铺js——2.2.1优化 (#1454)
* 2.2.1更新


### 2.2.1(2025.08.03)

-   加快锻造速度
-   优化通知逻辑,减少通知被吞概率

* Update main.js
2025-08-03 02:46:36 +08:00

371 lines
14 KiB
JavaScript

/*********************** 配置与常量 ***********************/
// 用户配置
let smithyName = settings.smithyName || "枫丹铁匠铺"; // 铁匠铺地区
let primaryOre = settings.ore || "水晶块"; // 主选矿石
let secondaryOre = settings.secondaryOre || "萃凝晶"; // 备选矿石1
let tertiaryOre = settings.tertiaryOre || "紫晶块"; // 备选矿石2
let notice = settings.notice ?? false; // 通知状态
let forgedOrNot = (settings.forgedOrNot && settings.forgedOrNot.trim() !== "") ? settings.forgedOrNot : "是"; // 是否锻造
// 矿石图像与中文名称映射
const ingredientImageMap = {
萃凝晶: "assets/Picture/CondessenceCrystal.png",
紫晶块: "assets/Picture/AmethystLump.png",
水晶块: "assets/Picture/CrystalChunk.png",
星银矿石: "assets/Picture/Starsilver.png",
白铁块: "assets/Picture/WhiteIronChunk.png",
铁块: "assets/Picture/IronChunk.png",
};
const OreChineseMap = {
萃凝晶: "萃凝晶",
紫晶块: "紫晶块",
水晶块: "水晶块",
星银矿石: "星银矿石",
白铁块: "白铁块",
铁块: "铁块",
};
// 模板识别对象
const ConfirmDeployButtonRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync("Assets/RecognitionObject/Confirm Deploy Button.png"),
0, 870, 1920, 210
); // 确定按钮
const ForgingInterfaceRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync("Assets/RecognitionObject/ForgingInterface.png"),
0, 0, 140, 100
); // 锻造界面图标
const ForgeRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync("Assets/RecognitionObject/Forge.png"),
1260, 300, 600, 600
); // 对话框中的锻造图标
// 计算矿物图标的坐标(行列排列)
const rows = [1, 2, 3];
const cols = [1, 2, 3, 4, 5];
const gridCoordinates = [];
for (const row of rows) {
for (const col of cols) {
const ProcessingX = Math.round(150 + (col - 1) * 145);
const ProcessingY = Math.round(230 + (row - 1) * 170);
gridCoordinates.push({ row, col, x: ProcessingX, y: ProcessingY });
}
}
/*********************** 工具函数 ***********************/
// 图像识别函数
function recognizeImage(imagePath, x, y, searchWidth, searchHeight) {
try {
let template = file.ReadImageMatSync(imagePath);
let recognitionObject = RecognitionObject.TemplateMatch(template, x, y, searchWidth, searchHeight);
recognitionObject.threshold = 0.85;
recognitionObject.Use3Channels = true;
let result = captureGameRegion().find(recognitionObject);
return result.isExist() ? result : null;
} catch (error) {
if (notice) {
notification.error(`图像识别失败,路径: ${imagePath}, 错误: ${error.message}`);
} else {
log.error(`图像识别失败,路径: ${imagePath}, 错误: ${error.message}`);
}
return null;
}
}
// 检查是否需要跳过该矿石(若已属于备选中)
function shouldSkipOre(targetOre, compareOres) {
return compareOres.includes(targetOre);
}
// 通知日志:使用矿石提示
function determineOre(oreType) {
let message = `将使用 ${OreChineseMap[oreType]} 锻造矿石`;
log.info(message);
return message;
}
/*********************** 主逻辑函数 ***********************/
// 自动前往铁匠铺
async function autoSmithy(smithyName) {
log.info(`自动前往 ${smithyName}`);
try {
let filePath = `assets/Pathing/${smithyName}.json`;
await pathingScript.runFile(filePath);
if (notice) {
notification.send(`已抵达 ${smithyName}`);
} else {
log.info(`已抵达 ${smithyName}`);
}
} catch (error) {
if (notice) {
notification.error(`执行 ${smithyName} 路径时发生错误: ${error.toString()}`);
} else {
log.error(`执行 ${smithyName} 路径时发生错误: ${error.toString()}`);
}
}
}
// 尝试识别并锻造矿石
async function tryForgeOre(oreType, skipCheckOres = []) {
// 若矿石在跳过列表中则直接返回
if (shouldSkipOre(oreType, skipCheckOres)) {
if (notice) {
//notification.send(`跳过 ${OreChineseMap[oreType]},因为已存在于优先选择中`);
}
return false;
}
// 获取矿石图像路径
const imagePath = ingredientImageMap[oreType];
if (!imagePath) {
if (notice) {
notification.error(`未找到矿石图像路径: ${OreChineseMap[oreType]}`);
} else {
log.error(`未找到矿石图像路径: ${OreChineseMap[oreType]}`);
}
return false;
}
log.info(`开始识别矿石: ${OreChineseMap[oreType]}`);
const scanOffset = { x: -35, y: -35 };
const maxAttempts = 3;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
let found = false;
for (const coordinate of gridCoordinates) {
const scanX = coordinate.x + scanOffset.x;
const scanY = coordinate.y + scanOffset.y;
const imageResult = recognizeImage(imagePath, scanX, scanY, 70, 70);
if (imageResult) {
found = true;
imageResult.click();
await sleep(100);
// if (notice) {
// notification.send(`找到矿石: ${OreChineseMap[oreType]}`);
// } else {
log.info(`找到矿石: ${OreChineseMap[oreType]}`);
// }
determineOre(oreType);
// 点击“开始锻造”按钮并进行OCR识别
const ocrRegion = { x: 660, y: 495, width: 1250 - 660, height: 550 - 495 };
let clickAttempts = 0;
let forgingTriggered = false;
while (clickAttempts < 4 && !forgingTriggered) {
let ConfirmButton = captureGameRegion().find(ConfirmDeployButtonRo);
if (ConfirmButton.isExist()) {
ConfirmButton.click();
clickAttempts++;
}
await sleep(200);
let ocrResults = captureGameRegion().find(
RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height)
);
if (ocrResults) {
// log.info(`${ocrResults.text}`);
if (ocrResults.text.includes("今日已无法锻造")) {
if (notice) {
notification.send("检测到 今日已无法锻造 结束锻造");
} else {
log.info("检测到 今日已无法锻造 结束锻造");
}
await sleep(100);
await click(960, 1042);
await sleep(200);
await click(960, 1042);// 多次点击结束弹窗
return true; // 终止锻造流程
}
else if (ocrResults.text.includes("材料不足")) {
if (notice) {
notification.send("检测到 材料不足 跳过当前矿物。请检查背包,及时补充矿物。");
} else {
log.info("检测到 材料不足 跳过当前矿物。请检查背包,及时补充矿物。");
}
clickAttempts--; // 出现材料不足时减去一次点击计数
await sleep(100);
await click(960, 1042);
await sleep(200);
await click(960, 1042);// 多次点击结束弹窗
return false; // 跳过当前矿石
}
}
if (clickAttempts === 4) {
await sleep(1000);
if (notice) {
notification.send("锻造已完成");
} else {
log.info("锻造已完成");
}
return true; // 达到点击上限,终止锻造流程
}
}
}
}
if (!found) {
log.error(`未能识别到矿石: ${OreChineseMap[oreType]},重试中... (${attempt + 1}/${maxAttempts})`);
await sleep(1000);
}
}
if (notice) {
notification.error(`未能识别到矿石: ${OreChineseMap[oreType]},停止尝试`);
} else {
log.error(`未能识别到矿石: ${OreChineseMap[oreType]},停止尝试`);
}
return false;
}
// 对话、领取、锻造操作
async function forgeOre(smithyName) {
// 对话部分
await sleep(1000);
keyPress("F");
await sleep(1000);
await click(960, 1042);
await sleep(1000);
await click(960, 1042);
// 搜索对话界面中的锻造图标
const maxAttempts = 3;
let dialogFound = false;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
for (let i = 0; i < 3; i++) {
let Forge = captureGameRegion().find(ForgeRo);
if (Forge.isExist()) {
log.info("已找到对话界面锻造图标");
await sleep(1000);
Forge.click();
dialogFound = true;
break;
} else {
await sleep(1000);
await click(960, 1042);
}
}
if (dialogFound) break;
}
// 检测锻造界面是否出现
if (dialogFound) {
let interfaceFound = false;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const ocrRegion = { x: 185, y: 125, width: 670 - 185, height: 175 - 125 };
let ocrResults = captureGameRegion().find(
RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height)
);
let innerFound = false;
for (let i = 0; i < 3; i++) {
let ForgingInterface = captureGameRegion().find(ForgingInterfaceRo);
if (ForgingInterface.isExist()) {
log.info("已进入锻造界面");
innerFound = true;
break;
} else {
await sleep(1000);
await click(960, 1042);
}
}
if (innerFound) {
interfaceFound = true;
// 领取操作:点击全部领取及确认领取
const ClaimAllRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync("Assets/RecognitionObject/全部领取.png"),
0, 900, 1920, 180
);
let ClaimAll = captureGameRegion().find(ClaimAllRo);
if (ClaimAll.isExist()) {
ClaimAll.click();
await sleep(500);
let ConfirmButton = captureGameRegion().find(ConfirmDeployButtonRo);
if (ConfirmButton.isExist()) {
ConfirmButton.click();
await sleep(500);
await click(220, 150);
await sleep(1000); // 点击进入锻造界面
} else {
log.warn("未能识别到确定按钮");
}
}
// 若设置为锻造,则依次尝试主选及备选矿石
if (forgedOrNot === "是") {
let forgeSuccess = false;
if (await tryForgeOre(primaryOre, [])) {
forgeSuccess = true;
} else if (await tryForgeOre(secondaryOre, [primaryOre])) {
forgeSuccess = true;
} else if (await tryForgeOre(tertiaryOre, [primaryOre, secondaryOre])) {
forgeSuccess = true;
} else {
if (notice) {
notification.error("所有备选矿石都未能识别,结束锻造");
} else {
log.error("所有备选矿石都未能识别,结束锻造");
}
}
}
// 退出锻造前判断配方,点击“锻造队列”,再次确认使用的矿物已经锻造结果
const ocrRegionAfter = { x: 185, y: 125, width: 670 - 185, height: 175 - 125 };
let ocrResultsAfter = captureGameRegion().find(
RecognitionObject.ocr(ocrRegionAfter.x, ocrRegionAfter.y, ocrRegionAfter.width, ocrRegionAfter.height)
);
if (ocrResultsAfter.text.includes("锻造队列")) {
await sleep(1000);//等待僵直
ocrResultsAfter.click();
await sleep(200);
ocrResultsAfter.click();
await sleep(500);
}
break; // 退出锻造界面检测循环
}
}
if (!interfaceFound) {
log.error("经过多次尝试,未能进入锻造界面");
}
} else {
log.error("未能找到对话界面锻造图标,无法进入锻造流程");
}
// 退出锻造界面并返回主界面
if (notice) {
notification.send("锻造结束,退出界面");
} else {
log.info("锻造结束,退出界面");
}
await genshin.returnMainUi();
}
/*********************** 主执行入口 ***********************/
(async function () {
// 初始化及前往铁匠铺
setGameMetrics(1920, 1080, 1.25);
await genshin.returnMainUi();
if (notice) {
notification.send("自动锻造矿石脚本开始");
} else {
log.info("自动锻造矿石脚本开始");
}
await autoSmithy(smithyName);
await forgeOre(smithyName);
await genshin.returnMainUi();
//后退两步
{ keyDown("S"); await sleep(1000); keyUp("S"); await sleep(1000); }
//if (notice) {
// notification.send("自动锻造矿石脚本结束");
//}
//else {
log.info("自动锻造矿石脚本结束");
//}
})();