归档重复或无用脚本,修改调整时间脚本 (#2662)

This commit is contained in:
躁动的小氨气
2026-01-12 15:27:06 +08:00
committed by GitHub
parent 7623cce2cc
commit 4a530748c7
61 changed files with 442 additions and 389 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,104 @@
const outDatedRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/ConfirmButton.png"), 760, 700, 100, 100);
(async function () {
// 读取用户设置
let operationType = settings.operationType || "分解(经验瓶)"; // 默认为分解
let times = settings.times || 1; // 默认为1
let includeOneStar = settings.includeOneStar || false;
let includeTwoStar = settings.includeTwoStar || false;
let includeThreeStar = settings.includeThreeStar || false;
let includeFourStar = settings.includeFourStar || false;
log.debug(`操作类型: ${operationType}`);
log.debug(`操作次数: ${times}`);
log.debug(`包含一星: ${includeOneStar}, 包含二星: ${includeTwoStar}, 包含三星: ${includeThreeStar}, 包含四星: ${includeFourStar}`);
// 点击过期按钮
async function findAndClick(target, maxAttempts = 20) {
for (let attempts = 0; attempts < maxAttempts; attempts++) {
const gameRegion = captureGameRegion();
try {
const result = gameRegion.find(target);
if (result.isExist) {
result.click();
return true; // 成功立刻返回
}
log.warn(`识别失败,第 ${attempts + 1} 次重试`);
} catch (err) {
} finally {
gameRegion.dispose();
}
if (attempts < maxAttempts - 1) { // 最后一次不再 sleep
await sleep(250);
}
}
return false;
}
// 分解圣遗物
async function salvage() {
await genshin.returnMainUi();
keyPress("B"); await sleep(2000); // 打开背包
if (await findAndClick(outDatedRo)) {
log.info("检测到过期物品弹窗,处理");
await sleep(1000);
}
click(670, 40); await sleep(1000); // 点击圣遗物
click(660, 1010); await sleep(1000); // 点击分解
click(300, 1020); await sleep(1000); // 点击快速选择
// 根据星级选择
if (!includeOneStar) click(200, 150); await sleep(500); // 不包含一星
if (!includeTwoStar) click(200, 220); await sleep(500); // 不包含二星
if (!includeThreeStar) click(200, 300); await sleep(500); // 不包含三星
if (!includeFourStar) click(200, 380); await sleep(500); // 不包含四星
click(340, 1000); await sleep(1000); // 点击确认选择
click(1720, 1015); await sleep(1500); // 点击分解
click(1180, 750); await sleep(1000); // 点击进行分解
click(1840, 45); await sleep(1500); // 取消
click(1840, 45); await sleep(1000); // 取消
click(1840, 45); await sleep(1000); // 取消
}
// 摧毁圣遗物
async function destroy() {
await genshin.returnMainUi();
keyPress("B"); await sleep(2000); // 打开背包
if (await findAndClick(outDatedRo)) {
log.info("检测到过期物品弹窗,处理");
await sleep(1000);
}
click(670, 40); await sleep(1000); // 点击圣遗物
for (let i = 0; i < times; i++) {
click(75, 1015); await sleep(1000); // 点击摧毁
click(165, 1020); await sleep(1000); // 点击快捷放入
// 根据星级选择
if (includeOneStar) click(200, 150); await sleep(500); // 包含一星
if (includeTwoStar) click(200, 220); await sleep(500); // 包含二星
if (includeThreeStar) click(200, 300); await sleep(500); // 包含三星
if (includeFourStar) click(200, 380); await sleep(500); // 包含四星
click(165, 1020); await sleep(1000); // 点击快捷放入
click(1720, 1015); await sleep(1000); // 点击摧毁
click(1200, 830); await sleep(1000); // 点击摧毁
click(960, 1000); await sleep(1000); // 点击空白处
}
click(1840, 45); await sleep(1500); // 取消
click(1840, 45); await sleep(1000); // 取消
click(1840, 45); await sleep(1000); // 取消
}
// 执行操作
if (operationType === "分解(经验瓶)") {
await salvage();
log.info("分解完成。");
} else if (operationType === "摧毁(摩拉)") {
await destroy();
log.info("摧毁完成。");
}
})();

View File

@@ -0,0 +1,15 @@
{
"manifest_version": 1,
"name": "自动分解或摧毁1-4星圣遗物",
"version": "1.0.2",
"bgi_version": "0.45.1",
"description": "自动分解或摧毁1-4星圣遗物可选择是否分解1-4星圣遗物。本脚本根据AutoArtifacts_A_B_Extra和DestroyArtifactsForMora修改而来作者还不会修bug如有问题请先自行解决。",
"authors": [
{
"name": "24qiaoyue",
"links": "https://github.com/24qiaoyue"
}
],
"settings_ui": "settings.json",
"main": "main.js"
}

View File

@@ -0,0 +1,36 @@
[
{
"name": "operationType",
"type": "select",
"label": "操作类型(默认:分解)",
"options": [
"分解(经验瓶)",
"摧毁(摩拉)"
]
},
{
"name": "times",
"type": "input-text",
"label": "操作次数,默认为1仅限摧毁一次摧毁最大数量为100个圣遗物"
},
{
"name": "includeOneStar",
"type": "checkbox",
"label": "包含一星圣遗物"
},
{
"name": "includeTwoStar",
"type": "checkbox",
"label": "包含二星圣遗物"
},
{
"name": "includeThreeStar",
"type": "checkbox",
"label": "包含三星圣遗物"
},
{
"name": "includeFourStar",
"type": "checkbox",
"label": "包含四星圣遗物"
}
]

View File

@@ -0,0 +1,70 @@
(async function () {
setGameMetrics(1920, 1080, 2); // 设置游戏窗口大小和DPI
//拖动鼠标
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('设置时间到{xy}点',settings.time);
await keyPress("Escape");
await sleep(1000);
await click(50,700);
await sleep(2000);
await settime(settings.time)
await sleep(3000);
await click(1500,1000);//确认
await sleep(20000);
await keyPress("Escape");
await sleep(2000);
await keyPress("Escape");
await sleep(2000);
})();

View File

@@ -0,0 +1,14 @@
{
"manifest_version": 1,
"name": "调整游戏时间",
"version": "1.0",
"description": "用于调整游戏时间,请在调度器中使用",
"authors": [
{
"name": "愚溪",
"links": "https://github.com/Kupder"
}
],
"settings_ui": "settings.json",
"main": "main.js"
}

View File

@@ -0,0 +1,7 @@
[
{
"name": "time",
"type": "input-text",
"label": "输入时间 [0-24] "
}
]

View File

@@ -0,0 +1,64 @@
(async function () {
// 设置游戏分辨率和DPI缩放
setGameMetrics(1920, 1080, 1);
// 圆心坐标
const centerX = 1441;
const centerY = 501.6;
// 半径
const r1 = 30;
const r2 = 150;
const r3 = 300;
const stepDuration = 50;
function getPosition(r, index) {
let angle = index * Math.PI / 720;
return [Math.round(centerX + r * Math.cos(angle)), Math.round(centerY + r * Math.sin(angle))];
}
async function mouseClick(x, y) {
moveMouseTo(x, y);
await sleep(50);
leftButtonDown();
await sleep(50);
leftButtonUp();
await sleep(stepDuration);
}
async function mouseClickAndMove(x1, y1, x2, y2) {
moveMouseTo(x1, y1);
await sleep(50);
leftButtonDown();
await sleep(50);
moveMouseTo(x2, y2);
await sleep(50);
leftButtonUp();
await sleep(stepDuration);
}
async function setTime(hour, minute) {
const end = (hour + 6) * 60 + minute-20;
const n = 3;
for (let i = - n + 1; i < 1; i++) {
let [x,y] = getPosition(r1, end + i * 1440 / n);
await mouseClick(x, y);
}
let [x1,y1] = getPosition(r2, end + 5);
let [x2, y2] = getPosition(r3, end + 20 + 0.5);
await mouseClickAndMove(x1, y1, x2, y2);
}
const hour = Number(settings.hour);
const minute = Number(settings.minute);
let h = Math.floor(hour+minute/60);
const m = Math.floor(hour*60+minute)-h*60;
h = ((h % 24) + 24) % 24;
log.info(`设置时间到 ${h}${m}`);
await keyPress("Escape");
await sleep(1000);
await click(50,700);
await sleep(2000);
await setTime(h, m);
await sleep(1000);
await click(1500,1000);//确认
await sleep(18000);
await keyPress("Escape");
await sleep(2000);
await keyPress("Escape");
await sleep(2000);
})();

View File

@@ -0,0 +1,15 @@
{
"manifest_version": 1,
"name": "精确调整游戏时间到分钟",
"version": "1.0",
"bgi_version": "0.36.0",
"description": "用于调整游戏时间,精确到分钟,请在调度器中使用",
"authors": [
{
"name": "Tim",
"links": "https://github.com/Limint"
}
],
"settings_ui": "settings.json",
"main": "main.js"
}

View File

@@ -0,0 +1,12 @@
[
{
"name": "hour",
"type": "input-text",
"label": "输入小时0-24"
},
{
"name": "minute",
"type": "input-text",
"label": "输入分钟0-60"
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,97 @@
// 自定义 basename 函数
function basename(filePath) {
const lastSlashIndex = filePath.lastIndexOf('\\'); // 或者使用 '/',取决于你的路径分隔符
return filePath.substring(lastSlashIndex + 1);
}
(async function () {
setGameMetrics(1920, 1080, 1); // 设置游戏分辨率
await genshin.returnMainUi();
keyPress("B"); await sleep(1000);
// 定义图标文件夹路径
const iconDir = "assets/icon";
const pictureDir = "assets/Picture";
const iconFilePaths = file.ReadPathSync(iconDir);
const pictureFilePaths = file.ReadPathSync(pictureDir);
const iconRecognitionObjects = [];
for (const filePath of iconFilePaths) {
const mat = file.readImageMatSync(filePath);
if (mat.empty()) {
log.error(`加载图标失败:${filePath}`);
continue;
}
const recognitionObject = RecognitionObject.TemplateMatch(mat, 0, 0, 1920, 1080);
iconRecognitionObjects.push({ name: basename(filePath), ro: recognitionObject });
}
const pictureRegions = [];
for (const filePath of pictureFilePaths) {
const mat = file.readImageMatSync(filePath);
if (mat.empty()) {
log.error(`加载图库失败:${filePath}`);
continue;
}
pictureRegions.push({ name: basename(filePath), region: new ImageRegion(mat, 0, 0) });
}
const foundRegions = [];
for (const picture of pictureRegions) {
for (const icon of iconRecognitionObjects) {
// 在图库中查找图标信息
const foundRegion = picture.region.find(icon.ro);
if (foundRegion.isExist()) {
foundRegions.push({
pictureName: picture.name,
iconName: icon.name,
region: foundRegion
});
}
}
}
for (const foundRegion of foundRegions) {
const ra = captureGameRegion();
const tolerance = 1; // 容错区间
const iconMat = file.ReadImageMatSync(`assets/icon/${foundRegion.iconName}`);
const recognitionObject = RecognitionObject.TemplateMatch(iconMat, foundRegion.region.x-tolerance, foundRegion.region.y-tolerance, foundRegion.region.width+2*tolerance, foundRegion.region.height+2*tolerance);
recognitionObject.threshold = 0.85; // 设置识别阈值为 0.9
const result = ra.find(recognitionObject);
if (result.isExist() && result.x > 0 && result.y > 0) {
// 获取图标的中心坐标并取整
const x = Math.round(foundRegion.region.x + foundRegion.region.width / 2);
const y = Math.round(foundRegion.region.y + foundRegion.region.height / 2);
await click(x, y);
log.info(`点击 ${foundRegion.iconName}成功,位置: (${x}, ${y})`);
await sleep(500); // 等待一段时间
} else {
// 如果未找到图标,打印警告信息
log.info(`未发现背包弹窗:${foundRegion.iconName}`);
}
ra.dispose();
}
const coords = [
[670, 40], [660, 1010], [300, 1020],
// [200, 150, 500], [200, 220, 500],
[200, 300, 500, settings.autoSalvage3 === '否'],
[200, 380, 500, settings.autoSalvage4 === '否'],
[340, 1000], [1720, 1015], [1320, 756],
[1840, 45, 1500], [1840, 45], [1840, 45]
];
for (const coord of coords) {
const [x, y, delay = 1000, condition = true] = coord; // 默认值处理
if (condition) {
click(x, y);
await sleep(delay);
}
}
})();

View File

@@ -0,0 +1,14 @@
{
"manifest_version": 1,
"name": "选择级别 分解圣遗物 背包过期弹窗版",
"version": "1.0",
"description": "可选是否分解三四级圣遗物,附带打开背包时,自动点击过期物品弹窗",
"authors": [
{
"name": "吉吉喵",
"links": "https://github.com/JJMdzh"
}
],
"settings_ui": "settings.json",
"main": "main.js"
}

View File

@@ -0,0 +1,20 @@
[
{
"name": "autoSalvage4",
"type": "select",
"label": "自动分解包括4星圣遗物默认",
"options": [
"是",
"否"
]
},
{
"name": "autoSalvage3",
"type": "select",
"label": "自动分解包括3星圣遗物默认",
"options": [
"是",
"否"
]
}
]

View File

@@ -0,0 +1,73 @@
// 定义 readFolder 函数
/*
该函数可以实现输入要处理的文件夹路径后,将其中所有文件/仅json文件按照原顺序存储在一个对象中具体使用参考主函数
*/
async function readFolder(folderPath, onlyJson) {
log.info(`开始读取文件夹:${folderPath}`);
// 新增一个堆栈,初始时包含 folderPath
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 {
// 如果是文件,根据 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
});
//log.info(`找到 JSON 文件:${filePath}`);
}
} else {
const fileName = filePath.split('\\').pop(); // 提取文件名
const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组
files.push({
fullPath: filePath,
fileName: fileName,
folderPathArray: folderPathArray
});
//log.info(`找到文件:${filePath}`);
}
}
}
// 将临时数组中的子文件夹路径按原顺序压入堆栈
folderStack.push(...subFolders.reverse()); // 反转子文件夹路径
}
return files;
}
// 主函数
(async function () {
// 调用 readFolder 函数测试
const folderPath = settings.folderPath; // 文件夹路径
const onlyJson = settings.onlyJson; // 是否只返回 JSON 文件
const files = await readFolder(folderPath, onlyJson);
log.info(`处理结果:`);
files.forEach(file => {
log.info(`完整路径:${file.fullPath}`);
log.info(`文件名:${file.fileName}`);
log.info(`文件夹路径数组:${file.folderPathArray.join(", ")}`);//允许通过访问该数组的不同层级,例如判断材料种类等
});
})();

View File

@@ -0,0 +1,14 @@
{
"manifest_version": 1,
"name": "多层文件夹处理",
"version": "1.0",
"description": "读取多层文件夹并进行处理的一种方法",
"authors": [
{
"name": "mno",
"links": "https://github.com/Bedrockx"
}
],
"settings_ui": "settings.json",
"main": "main.js"
}

View File

@@ -0,0 +1,14 @@
[
{
"name": "folderPath",
"type": "input-text",
"label": "要处理的文件夹在js文件夹中的路径",
"default": "assets"
},
{
"name": "onlyJson",
"type": "checkbox",
"label": "是否只处理json文件"
}
]

View File

@@ -0,0 +1,81 @@
(async function () {
// 设置游戏分辨率和DPI缩放
setGameMetrics(1920, 1080, 1);
// 圆心坐标
const centerX = 1441;
const centerY = 501.6;
// 半径
const r1 = 30;
const r2 = 150;
const r3 = 300;
const stepDuration = 50;
function getPosition(r, index) {
let angle = index * Math.PI / 720;
return [Math.round(centerX + r * Math.cos(angle)), Math.round(centerY + r * Math.sin(angle))];
}
async function mouseClick(x, y) {
moveMouseTo(x, y);
await sleep(50);
leftButtonDown();
await sleep(50);
leftButtonUp();
await sleep(stepDuration);
}
async function mouseClickAndMove(x1, y1, x2, y2) {
moveMouseTo(x1, y1);
await sleep(50);
leftButtonDown();
await sleep(50);
moveMouseTo(x2, y2);
await sleep(50);
leftButtonUp();
await sleep(stepDuration);
}
async function setTime(hour, minute) {
const end = (hour + 6) * 60 + minute-20;
const n = 3;
for (let i = - n + 1; i < 1; i++) {
let [x,y] = getPosition(r1, end + i * 1440 / n);
await mouseClick(x, y);
}
let [x1,y1] = getPosition(r2, end + 5);
let [x2, y2] = getPosition(r3, end + 20 + 0.5);
await mouseClickAndMove(x1, y1, x2, y2);
}
async function cancelAni() {
moveMouseTo(200, 200);
leftButtonDown();
await(10);
leftButtonUp();
}
const hour = Number(settings.hour);
const minute = Number(settings.minute);
let h = Math.floor(hour+minute/60);
const m = Math.floor(hour*60+minute)-h*60;
h = ((h % 24) + 24) % 24;
log.info(`设置时间到 ${h}${m}`);
await keyPress("Escape");
await sleep(1000);
await click(50,700);
await sleep(1000);
await setTime(h, m);
await sleep(500);
// 点击确认
await click(1500,1000);
// 跳过调整动画
await sleep(1);
await cancelAni();
await sleep(1000);
await click(45,715);
// 重新进入调时间界面以消除调时间的声音
await sleep(600);
await keyPress("Escape");
// 退出派蒙界面
await sleep(600);
await keyPress("Escape");
})();

View File

@@ -0,0 +1,18 @@
{
"manifest_version": 1,
"name": "精确调整游戏时间到分钟,跳过调整动画",
"version": "1.0",
"bgi_version": "0.36.0",
"description": "用于调整游戏时间,精确到分钟,请在调度器中使用",
"authors": [
{
"name": "Tim",
"links": "https://github.com/Limint"
},
{
"name": "子寻"
}
],
"settings_ui": "settings.json",
"main": "main.js"
}

View File

@@ -0,0 +1,12 @@
[
{
"name": "hour",
"type": "input-text",
"label": "输入小时0-24"
},
{
"name": "minute",
"type": "input-text",
"label": "输入分钟0-60"
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1,22 @@
{
"macroEvents": [
{"type": 2, "mouseX": 1200, "mouseY": 901, "time": 0},
{"type": 2, "mouseX": 1200, "mouseY": 801, "time": 50},
{"type": 2, "mouseX": 1200, "mouseY": 701, "time": 100},
{"type": 2, "mouseX": 1200, "mouseY": 601, "time": 150},
{"type": 2, "mouseX": 1200, "mouseY": 501, "time": 200},
{"type": 2, "mouseX": 1200, "mouseY": 401, "time": 250},
{"type": 2, "mouseX": 1200, "mouseY": 301, "time": 300},
{"type": 2, "mouseX": 1200, "mouseY": 201, "time": 350},
{"type": 2, "mouseX": 1200, "mouseY": 200, "mouseButton": "Left", "time": 400}
],
"info": {
"name": "",
"description": "",
"x": 0,
"y": 0,
"width": 1920,
"height": 1080,
"recordDpi": 1
}
}

View File

@@ -0,0 +1,22 @@
{
"macroEvents": [
{"type": 2, "mouseX": 1200, "mouseY": 901, "time": 0},
{"type": 2, "mouseX": 1200, "mouseY": 801, "time": 50},
{"type": 2, "mouseX": 1200, "mouseY": 701, "time": 100},
{"type": 2, "mouseX": 1200, "mouseY": 601, "time": 150},
{"type": 2, "mouseX": 1200, "mouseY": 501, "time": 200},
{"type": 2, "mouseX": 1200, "mouseY": 401, "time": 250},
{"type": 2, "mouseX": 1200, "mouseY": 301, "time": 300},
{"type": 2, "mouseX": 1200, "mouseY": 205, "time": 350},
{"type": 2, "mouseX": 1200, "mouseY": 204, "mouseButton": "Left", "time": 400}
],
"info": {
"name": "",
"description": "",
"x": 0,
"y": 0,
"width": 1920,
"height": 1080,
"recordDpi": 1
}
}

View File

@@ -0,0 +1,3 @@
{"macroEvents":[{"type":6,"mouseX":0,"mouseY":-120,"time":0},
{"type":6,"mouseX":0,"mouseY":0,"time":100}],
"info":{"name":"","description":"","x":0,"y":0,"width":1920,"height":1080,"recordDpi":1}}

View File

@@ -0,0 +1,29 @@
{
"info": {
"name": "璃月杂货商东升旁灶台",
"type": "collect",
"author": "吉吉喵",
"version": "1.0",
"description": "",
"bgiVersion": "0.42.3"
},
"positions": [
{
"id": 1,
"x": 267.92,
"y": -665.01,
"action": "force_tp",
"move_mode": "walk",
"type": "teleport"
},
{
"id": 2,
"x": 255.099609375,
"y": -680.1826171875,
"type": "target",
"move_mode": "walk",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,488 @@
(async function () {
// 设置游戏基础参数
setGameMetrics(1920, 1080, 1);
await genshin.returnMainUi();
/**菜单区**/
// 初始化变量
let CookingClickX; // 烹饪点击坐标 X
const CookingClickY = 45; // 烹饪点击坐标 Y假设固定为 45
/**加工区**/
// 食材图像映射
const ingredientImageMap = {
"Processing11": "assets/Picture/Flour.png",
"Processing12": "assets/Picture/Raw-Meat.png",
"Processing13": "assets/Picture/Fish.png",
"Processing14": "assets/Picture/Mysterious-Meat.png",
"Processing15": "assets/Picture/Rye-Flour.png",
"Processing16": "assets/Picture/Butter.png",
"Processing17": "assets/Picture/Smoked-Poultry.png",
"Processing18": "assets/Picture/Lard.png",
"Processing21": "assets/Picture/Ham.png",
"Processing22": "assets/Picture/Sugar.png",
"Processing23": "assets/Picture/Spices.png",
"Processing24": "assets/Picture/Sour-Cream.png",
"Processing25": "assets/Picture/Crab-Roe.png",
"Processing26": "assets/Picture/Jam.png",
"Processing27": "assets/Picture/Cheese.png",
"Processing28": "assets/Picture/Bacon.png",
"Processing31": "assets/Picture/Sausage.png",
// 添加其他食材的图像映射
};
// processingKey 映射为中文
const processingKeyChineseMap = {
"Processing11": "面粉",
"Processing12": "兽肉",
"Processing13": "鱼",
"Processing14": "神秘的肉",
"Processing15": "黑麦粉",
"Processing16": "奶油",
"Processing17": "熏禽肉",
"Processing18": "黄油",
"Processing21": "火腿",
"Processing22": "糖",
"Processing23": "香辛料",
"Processing24": "酸奶油",
"Processing25": "蟹黄",
"Processing26": "果酱",
"Processing27": "奶酪",
"Processing28": "培根",
"Processing31": "香肠"
// 添加其他加工设置的中文映射
};
// 解析 加工数量PrepCount 的值,逗号分隔的数字序列
const PrepCountArray = (settings.PrepCount || "")
.split(",")
.filter(Boolean) // 过滤掉空字符串
.map((num) => Math.max(0, Number(num) || 0)); // 转换为非负整数数组
// 提取所有 ProcessingXX 的键名,并过滤出值为 true 的项
const enabledProcessingKeys = Object.keys(settings)
.filter(key => key.startsWith("Processing"))
.filter(key => settings[key]); // 确保值为 true
// log.info(`启用的加工项: ${enabledProcessingKeys.join(", ")}`);
// 将 enabledProcessingKeys 映射为中文描述
const enabledProcessingKeysChinese = enabledProcessingKeys.map(key => processingKeyChineseMap[key] || "未知食材");
log.info(`启用的加工项: ${enabledProcessingKeysChinese.join(", ")}`);
if (enabledProcessingKeys.length === 0) {
log.error("未找到启用的 Processing 设置");
// 如果没有启用的 Processing 设置,假设所有 Processing 设置都为 true
for (let i = 1; i <= 4; i++) {
for (let j = 1; j <= 8; j++) {
const processingKey = `Processing${i}${j}`;
settings[processingKey] = false; // 如果没有启用的 ProcessingXX则设置所有 ProcessingXX都为 false
// enabledProcessingKeys.push(processingKey);// 如果没有启用的 ProcessingXX则所有 ProcessingXX都为 true
}
}
}
// 行列数的排列组合
const rows = [1, 2, 3];// [1, 2, 3, 4];三、四行 暂时用不上
const cols = [1, 2, 3, 4, 5, 6, 7, 8];
const gridCoordinates = [];
// 计算每个行列组合的坐标
for (const row of rows) {
for (const col of cols) {
const ProcessingX = Math.round(178.5 + (col - 1) * 146);
const ProcessingY = Math.round(182.5 + (row - 1) * 175);
gridCoordinates.push({ row, col, x: ProcessingX, y: ProcessingY });
}
}
// log.info(`生成的搜索区域坐标: ${JSON.stringify(gridCoordinates)}`);
// 图像识别函数
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.9; // 设置识别阈值为 0.9
recognitionObject.Use3Channels = true; // 使用三通道匹配
ra = captureGameRegion();
let result = ra.find(recognitionObject);
ra.dispose();
return result.isExist() ? result : null;
} catch (error) {
log.error(`图像识别失败,路径: ${imagePath}, 错误: ${error.message}`);
return null;
}
}
// 执行额外的点击操作
async function performExtraClicks(processingKey) {
if (processingKey === "Processing13") {
log.info("为 Processing13 执行额外的点击操作");
click(1715, 565); await sleep(1000); // 打开素材列表
click(220, 295); await sleep(1000); // 非红色花鳉一般位于第一行,默认选择第二行
click(1005, 45); await sleep(500)
click(1005, 45); // 点击菜单防止其他页面干扰
}
}
// 执行 PrepCount 操作
async function performPrepCountActions(PrepCount) {
log.info(`加工数量: ${PrepCount}`);
if (PrepCount < 99) {
click(965, 455); // 输入数量
await sleep(1000);
// 逐个字符输入 PrepCount
const PrepCountStr = String(PrepCount);
for (const char of PrepCountStr) {
keyPress(char);
await sleep(500);
}
} else if (PrepCount === 99) {
click(1195, 590); await sleep(1000);
}
click(1315, 755); await sleep(2000); // 点击确认
}
/**烹饪区**/
// 检查 CookingTimes 是否为正整数
const CookingTimes = Math.max(0, Number(settings.CookingTimes) || 0);
const Cooking = settings.Cooking
const rightOffset = 0; // 右偏移
const downOffset = 0; // 下偏移
if (CookingTimes > 0 && Number.isInteger(CookingTimes)) {
CookingClickX = 910; // 设置 CookingClickX
log.info("烹饪次数为正整数,有效");
} else {
log.info("烹饪次数不对,跳过烹饪操作。");
}
// 烹饪操作函数
async function performCookingOperations(CookingClickX, CookingClickY, rightOffset, downOffset, CookingTimes) {
log.info("执行烹饪操作...");
click(CookingClickX, CookingClickY);
await sleep(500);
click(CookingClickX, CookingClickY); // 点击菜单
await sleep(500);
click(145, 1015); // 点击筛选
await sleep(500);
click(79, 1020); // 重置输入框
await sleep(500);
click(160, 115); // 点击输入框
await sleep(500);
inputText(`${Cooking}`); // 输入 Cooking 的值
await sleep(500);
click(375, 1020); // 确认筛选
await sleep(500);
// 点击动态坐标
const rightClickX = Math.round(178.5 + rightOffset * 147);
const downClickY = Math.round(197 + downOffset * 176);
click(rightClickX, downClickY); // 点击选中物品的坐标
await sleep(1000);
click(1600, 1020); await sleep(3000);
click(635, 1015); await sleep(1000);
if (CookingTimes < 99) {
click(965, 455); // 输入数量
await sleep(1000);
// 逐个字符输入 CookingTimes
const CookingTimesStr = String(CookingTimes);
for (const char of CookingTimesStr) {
keyPress(char);
await sleep(500);
}
} else if (CookingTimes === 99) {
click(1190, 590); await sleep(1000);
}
click(1315, 755); await sleep(3000); // 点击自动烹饪
click(960, 905); await sleep(2000); // 点击确认
click(1845, 45); await sleep(200); // 退出烹饪界面
}
/**
F交互区
**/
// 定义一个函数用于模拟按键操作
async function simulateKeyOperations(key, duration) {
keyDown(key);
await sleep(duration);
keyUp(key);
await sleep(500); // 释放按键后等待 500 毫秒
}
// 识别 F 图标
async function recognizeFIcon() {
const fDialogueRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync("assets/F_Dialogue.png"),
1101,
400,
36,
400
);
ra = captureGameRegion();
let fRes = ra.find(fDialogueRo);
ra.dispose();
if (!fRes.isExist()) {
let f_attempts = 0; // 初始化尝试次数
while (f_attempts < 3) { // 最多尝试 3 次
f_attempts++;
log.info(`当前尝试次数:${f_attempts}`);
if (f_attempts === 1 || f_attempts === 2) {
// 第一次未找到 F 图标
await simulateKeyOperations("S", 200); // 后退 200 毫秒
log.info("执行后退操作");
await sleep(200);
await simulateKeyOperations("W", 600); // 前进 600 毫秒
log.info("执行前进操作");
} else if (f_attempts === 3) {
// 第二次未找到 F 图标
log.info("重新加载路径文件");
const filePath = `assets/璃月杂货商东升旁灶台.json`;
log.info(`加载路径文件:${filePath}`);
await pathingScript.runFile(filePath);
await sleep(500);
}
// 重新获取游戏区域截图
ra = captureGameRegion();
fRes = ra.find(fDialogueRo);
ra.dispose();
// 打印识别结果
log.info(`识别结果:${fRes.isExist()}`);
// 如果识别成功,立即退出循环
if (fRes.isExist()) {
log.info("识别成功,退出循环");
break;
}
await sleep(500);
}
}
// 如果尝试次数已达上限,返回 null
if (!fRes.isExist()) {
log.error("尝试次数已达上限");
return null;
}
return fRes;
}
// 识别 Cooking 图标
async function recognizeCookingIcon(centerYF) {
const cookingRo = RecognitionObject.TemplateMatch(
file.ReadImageMatSync("assets/Cooking.png"),
1176,
centerYF - 18, // 以 F 图标的中心,向上偏移 18 像素
27, // 宽度范围
36 // 高度范围
);
ra = captureGameRegion();
let cookingRes = ra.find(cookingRo);
ra.dispose();
if (cookingRes.isExist()) {
log.info("找到 灶台 图标");
return cookingRes;
} else {
log.info("未找到 灶台 图标");
return null;
}
}
// 主逻辑函数
async function main() {
// 识别 F 图标
let fRes = await recognizeFIcon();
if (!fRes) {
log.error("未能识别 F 图标,退出任务");
return;
}
// 获取 F 图标的中心点 Y 坐标
let centerYF = Math.round(fRes.y + fRes.height / 2);
const maxScrollAttempts = 5; // 最大滚轮操作次数限制
let scrollAttempts = 0;
while (scrollAttempts < maxScrollAttempts) {
// 识别 灶台 图标
let cookingRes = await recognizeCookingIcon(centerYF);
if (cookingRes) {
// log.info("找到 灶台 图标");
return cookingRes; // 识别成功,返回结果
}
// 如果未找到 Cooking 图标,执行滚轮操作
log.info(`未找到 Cooking 图标,执行滚轮操作,当前尝试次数:${scrollAttempts + 1}`);
await keyMouseScript.runFile(`assets/滚轮下翻.json`);
await sleep(1000);
// 重新识别 F 图标,获取最新的中心点
fRes = await recognizeFIcon();
if (!fRes) {
log.error("滚轮操作后,未能重新识别 F 图标,退出任务");
return;
}
centerYF = Math.round(fRes.y + fRes.height / 2); // 更新 F 图标的中心点 Y 坐标
// 增加尝试次数
scrollAttempts++;
}
// 如果超过最大滚轮操作次数,返回 null
log.error(`滚轮操作次数已达上限 ${maxScrollAttempts} 次,未能找到 Cooking 图标`);
return null;
}
// 自动寻路并执行按键操作
async function AutoPath() {
log.info("开始执行自动寻路任务");
// 定义路径文件路径
const filePath = `assets/璃月杂货商东升旁灶台.json`;
log.info(`加载路径文件:${filePath}`);
try {
// 执行路径文件
await pathingScript.runFile(filePath);
log.info("路径文件执行完成");
} catch (error) {
log.error(`执行路径文件时发生错误:${error.message}`);
return; // 如果路径文件执行失败,直接返回
}
// 等待1秒后执行按键操作
log.info("等待1秒后执行按键操作...");
await sleep(1000);
try {
// 识别 Cooking 图标
const cookingRes = await main();
if (!cookingRes) {
log.error("未能识别 灶台 图标,退出任务");
return;
}
// 按下 F 键
keyPress("F");
await sleep(1000);
// 加工菜单
click(1005, 45);
await sleep(1000);
click(150, 1020); await sleep(500);
click(150, 1020); await sleep(500);
// 如果 PrepCountArray 和 enabledProcessingKeys 的长度不匹配,使用第一个 PrepCount 值
const minLen = Math.min(PrepCountArray.length, enabledProcessingKeys.length);
const defaultPrepCount = PrepCountArray[0] || 99; // 默认值为第一个 PrepCount 或 99
// 遍历启用的 Processing 设置,进行图像识别
for (const processingKey of enabledProcessingKeys) {
// 获取 processingKey 的中文描述
const chineseDescription = processingKeyChineseMap[processingKey] || "未知食材";
const imagePath = ingredientImageMap[processingKey];
if (!imagePath) {
log.error(`未找到食材图像路径: ${chineseDescription}`);
continue;
}
// log.info(`开始识别食材: ${chineseDescription}, 图像路径: ${imagePath}`);
log.info(`开始识别食材: ${chineseDescription}`);
// 左上角的偏移量
const scanOffset = { x: -35, y: -35 };
let foundIngredient = false;
for (const coordinate of gridCoordinates) {
const scanX = coordinate.x + scanOffset.x; // 左上角的 X 坐标
const scanY = coordinate.y + scanOffset.y; // 左上角的 Y 坐标
const imageResult = recognizeImage(imagePath, scanX, scanY, 70, 70);
if (imageResult) {
// log.info(`通过图像识别找到食材: ${chineseDescription} 在坐标 X=${scanX}, Y=${scanY}`);
log.info(`通过图像识别找到食材: ${chineseDescription}`);
imageResult.click();
await sleep(1000); // 等待1秒以确保点击生效
imageResult.click(); // 重复点击,防止上一个食材加工满了的横幅导致没点击成功
await sleep(1000);
foundIngredient = true;
break; // 找到食材后跳出循环
}
}
if (!foundIngredient) {
// 图像识别未成功可能是因为该食材正在被加工通过OCR识别
ra = captureGameRegion();
const ocrResult = ra.findMulti(RecognitionObject.ocr(117, 196, 1148, 502));
ra.dispose();
for (var i = 0; i < ocrResult.count; ++i) {
if (ocrResult[i].text.endsWith("分钟") || ocrResult[i].text.endsWith("秒")) {
// 选中该食材
click(ocrResult[i].x, ocrResult[i].y);
await sleep(500);
// OCR食材名称
ra = captureGameRegion();
const ocrResult2 = ra.findMulti(RecognitionObject.ocr(1332, 130, 137, 34));
ra.dispose();
const ingredientName = ocrResult2.count > 0 ? ocrResult2[0].text : null;
if (ingredientName === chineseDescription) {
log.info(`通过OCR识别找到食材: ${chineseDescription}`);
foundIngredient = true;
break;
}
}
}
}
if (foundIngredient) {
// 执行额外的点击操作(如果需要)
await performExtraClicks(processingKey);
// 点击确认按钮
click(1600, 1020); await sleep(2000);
// 执行 PrepCount 操作
const PrepCount = PrepCountArray.length > 0 ? PrepCountArray.shift() : defaultPrepCount;
if (PrepCount > 0) {
await performPrepCountActions(PrepCount);
}
} else {
log.error(`未能识别到食材: ${chineseDescription}`);
}
}
// 如果 CookingClickX 被设置,执行烹饪操作
if (CookingClickX === 910) {
await performCookingOperations(CookingClickX, CookingClickY, rightOffset, downOffset, CookingTimes);
}
await genshin.returnMainUi();
keyDown("S"); await sleep(1000);
keyUp("S"); await sleep(1000);
} catch (error) {
log.error(`执行按键或鼠标操作时发生错误:${error.message}`);
}
}
let ra = null
// 调用 AutoPath 函数
await AutoPath();
})();

View File

@@ -0,0 +1,21 @@
{
"manifest_version": 1,
"name": "选择加工食材",
"version": "2.3",
"description": "食材加工识图复选框。适配黑麦粉、酸奶油",
"authors": [
{
"name": "吉吉喵",
"links": "https://github.com/JJMdzh"
}
],
"tags": [
"食材加工",
"图像识别",
"模板匹配",
"OCR",
"复选框"
],
"settings_ui": "settings.json",
"main": "main.js"
}

View File

@@ -0,0 +1,102 @@
[
{
"name": "CookingTimes",
"type": "input-text",
"label": "【料理制作】:\n\n烹饪次数默认0即跳过",
},
{
"name": "Cooking",
"type": "input-text",
"label": "输入料理名",
},
{
"name": "PrepCount",
"type": "input-text",
"label": "\n====================\n\n【食材加工】\n\n加工数量(不填默认99)"
},
{
"name": "Processing11",
"type": "checkbox",
"label": "如勾选:面粉,黄油,糖\n上方填入88,66\n则加工面粉88,黄油66,糖88\n-----------------\n\n1小麦=面粉"
},
{
"name": "Processing15",
"type": "checkbox",
"label": "-----------------\n\n1黑麦=黑麦粉"
},
{
"name": "Processing24",
"type": "checkbox",
"label": "-----------------\n\n1牛奶=酸奶油"
},
{
"name": "Processing16",
"type": "checkbox",
"label": "-----------------\n\n1牛奶=奶油"
},
{
"name": "Processing18",
"type": "checkbox",
"label": "-----------------\n\n2牛奶=黄油"
},
{
"name": "Processing27",
"type": "checkbox",
"label": "-----------------\n\n3牛奶=奶酪"
},
{
"name": "Processing21",
"type": "checkbox",
"label": "-----------------\n\n2兽肉+1盐=火腿"
},
{
"name": "Processing31",
"type": "checkbox",
"label": "-----------------\n\n3兽肉=香肠"
},
{
"name": "Processing17",
"type": "checkbox",
"label": "--------------------\n\n3禽肉+1盐=熏禽肉"
},
{
"name": "Processing28",
"type": "checkbox",
"label": "-----------------\n\n4兽肉+2盐=培根"
},
{
"name": "Processing22",
"type": "checkbox",
"label": "-----------------\n\n2甜甜花=糖"
},
{
"name": "Processing23",
"type": "checkbox",
"label": "-----------------\n\n2香辛果=香辛料"
},
{
"name": "Processing26",
"type": "checkbox",
"label": "----------------------------\n\n3日落果+2树莓+1糖=果酱"
},
{
"name": "Processing25",
"type": "checkbox",
"label": "-----------------\n\n4螃蟹=蟹黄"
},
{
"name": "Processing12",
"type": "checkbox",
"label": "--------------------\n\n冷鲜肉 转化成 兽肉"
},
{
"name": "Processing14",
"type": "checkbox",
"label": "-----------------------------\n\n神秘的肉 转化成 肉加工产物"
},
{
"name": "Processing13",
"type": "checkbox",
"label": "--------------------------------\n\n鱼 转化成 鱼肉,排除 红色花鳉"
}
]