feat: 通过键鼠自动到锚点,自动放置锚点 (#930)

* feat: 通过键鼠自动到锚点,自动放置锚点

* Update repo/js/FeatherFalling/settings.json

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
秋云
2025-05-27 00:38:22 +08:00
committed by GitHub
parent 33dd2c2533
commit 6beeecc0fb
6 changed files with 487 additions and 23 deletions

View File

@@ -1233,7 +1233,7 @@ async function adjustViewForReward(boxIconRo, token) {
let captureRegion = captureGameRegion();
let iconRes = captureRegion.Find(boxIconRo);
if (!iconRes) {
if (!iconRes.isExist()) {
throw new Error('未找到图标,没有地脉花');
}
@@ -1291,7 +1291,7 @@ async function closeCustomMarks() {
await sleep(600);
let button = captureGameRegion().find(openRo);
if (button) {
if (button.isExist()) {
marksStatus = false;
log.info("关闭自定义标记");
click(Math.round(button.x + button.width / 2), Math.round(button.y + button.height / 2));
@@ -1311,8 +1311,8 @@ async function openCustomMarks() {
click(60, 1020);
await sleep(600);
let button = captureGameRegion().findMulti(closeRo);
if (button) {
let button = captureGameRegion().find(closeRo);
if (button.isExist()) {
for (let i = 0; i < button.count; i++) {
let b = button[i];
if (b.y > 280 && b.y < 350) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,363 @@
{
"macroEvents": [
{
"type": 0,
"keyCode": 83,
"mouseX": 0,
"mouseY": 0,
"time": 1407
},
{
"type": 1,
"keyCode": 83,
"mouseX": 0,
"mouseY": 0,
"time": 1719
},
{
"type": 4,
"mouseX": 1919,
"mouseY": 1079,
"mouseButton": "Middle",
"time": 2469
},
{
"type": 5,
"mouseX": 1919,
"mouseY": 1079,
"mouseButton": "Middle",
"time": 2641
},
{
"type": 0,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 3438
},
{
"type": 1,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 4438
},
{
"type": 0,
"keyCode": 87,
"mouseX": 0,
"mouseY": 0,
"time": 5000
},
{
"type": 0,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 8328
},
{
"type": 1,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 8797
},
{
"type": 1,
"keyCode": 87,
"mouseX": 0,
"mouseY": 0,
"time": 12250
},
{
"type": 0,
"keyCode": 87,
"mouseX": 0,
"mouseY": 0,
"time": 12375
},
{
"type": 1,
"keyCode": 87,
"mouseX": 0,
"mouseY": 0,
"time": 12438
},
{
"type": 0,
"keyCode": 65,
"mouseX": 0,
"mouseY": 0,
"time": 13735
},
{
"type": 1,
"keyCode": 65,
"mouseX": 0,
"mouseY": 0,
"time": 13860
},
{
"type": 4,
"mouseX": 1919,
"mouseY": 1079,
"mouseButton": "Middle",
"time": 14391
},
{
"type": 5,
"mouseX": 1919,
"mouseY": 1079,
"mouseButton": "Middle",
"time": 14532
},
{
"type": 0,
"keyCode": 87,
"mouseX": 0,
"mouseY": 0,
"time": 15172
},
{
"type": 0,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 15453
},
{
"type": 1,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 15766
},
{
"type": 0,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 16375
},
{
"type": 1,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 16844
},
{
"type": 4,
"mouseX": 1919,
"mouseY": 1079,
"mouseButton": "Right",
"time": 17766
},
{
"type": 5,
"mouseX": 1919,
"mouseY": 1079,
"mouseButton": "Right",
"time": 17938
},
{
"type": 0,
"keyCode": 65,
"mouseX": 0,
"mouseY": 0,
"time": 18203
},
{
"type": 1,
"keyCode": 87,
"mouseX": 0,
"mouseY": 0,
"time": 18703
},
{
"type": 0,
"keyCode": 87,
"mouseX": 0,
"mouseY": 0,
"time": 19000
},
{
"type": 1,
"keyCode": 65,
"mouseX": 0,
"mouseY": 0,
"time": 19063
},
{
"type": 0,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 19344
},
{
"type": 1,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 19750
},
{
"type": 4,
"mouseX": 1919,
"mouseY": 1079,
"mouseButton": "Right",
"time": 19782
},
{
"type": 5,
"mouseX": 1919,
"mouseY": 1079,
"mouseButton": "Right",
"time": 19953
},
{
"type": 4,
"mouseX": 1919,
"mouseY": 1079,
"mouseButton": "Right",
"time": 20828
},
{
"type": 5,
"mouseX": 1919,
"mouseY": 1079,
"mouseButton": "Right",
"time": 20953
},
{
"type": 0,
"keyCode": 49,
"mouseX": 0,
"mouseY": 0,
"time": 21063
},
{
"type": 1,
"keyCode": 49,
"mouseX": 0,
"mouseY": 0,
"time": 21235
},
{
"type": 0,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 21672
},
{
"type": 0,
"keyCode": 65,
"mouseX": 0,
"mouseY": 0,
"time": 21922
},
{
"type": 1,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 21938
},
{
"type": 0,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 22047
},
{
"type": 1,
"keyCode": 65,
"mouseX": 0,
"mouseY": 0,
"time": 22094
},
{
"type": 0,
"keyCode": 65,
"mouseX": 0,
"mouseY": 0,
"time": 22157
},
{
"type": 1,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 22172
},
{
"type": 0,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 22250
},
{
"type": 1,
"keyCode": 65,
"mouseX": 0,
"mouseY": 0,
"time": 22297
},
{
"type": 0,
"keyCode": 65,
"mouseX": 0,
"mouseY": 0,
"time": 22375
},
{
"type": 1,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 22391
},
{
"type": 0,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 22438
},
{
"type": 1,
"keyCode": 65,
"mouseX": 0,
"mouseY": 0,
"time": 22485
},
{
"type": 1,
"keyCode": 68,
"mouseX": 0,
"mouseY": 0,
"time": 22547
},
{
"type": 1,
"keyCode": 87,
"mouseX": 0,
"mouseY": 0,
"time": 22719
}
],
"info": {
"name": "",
"description": "",
"x": 958,
"y": 562,
"width": 1920,
"height": 1080,
"recordDpi": 1.75
}
}

View File

@@ -1,10 +1,15 @@
const tpIconRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/icon/PortableWaypoint.png"));
const goTeleportRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/icon/GoTeleport.png"));
const tpBagRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/icon/PortableWaypointBag.png"));
const swimStateRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/icon/SwimState.png"),
1400, 900, 1920-1400, 1080-900);
1400, 900, 1920 - 1400, 1080 - 900);
const ocrRo = RecognitionObject.ocr(1420, 320, 500, 80);
tpIconRo.threshold = 0.70;
tpIconRo.Use3Channels = true;
swimStateRo.threshold = 0.50;
tpBagRo.threshold = 0.70;
tpBagRo.Use3Channels = true;
swimStateRo.threshold = 0.60;
(async function () {
await FeatherFailing();
})();
@@ -16,10 +21,12 @@ async function switchPartyIfNeeded(partyName) {
}
try {
log.info("正在尝试切换至" + partyName);
if(!await genshin.switchParty(partyName)){
if (!await genshin.switchParty(partyName)) {
log.info("切换队伍失败,前往七天神像重试");
await genshin.tpToStatueOfTheSeven();
await genshin.switchParty(partyName);
return await genshin.switchParty(partyName);
} else {
return true;
}
} catch {
log.error("队伍切换失败,可能处于联机模式或其他不可切换状态");
@@ -28,6 +35,29 @@ async function switchPartyIfNeeded(partyName) {
}
}
async function PlacePortableWaypoint() {
log.info("正在放置锚点");
await genshin.returnMainUi();
await sleep(500);
keyPress("B");
await sleep(500);
click(1050, 50);
await sleep(50);
click(1050, 50);
await sleep(1000);
let result = captureGameRegion().find(tpBagRo);
if(result.isExist()) {
result.click();
await sleep(1000);
click(1680, 990);
await sleep(50);
click(1680, 990);
} else {
throw new Error("锚点图标未识别,请制作口袋锚点");
}
await sleep(500);
}
async function FeatherFailing() {
await genshin.tpToStatueOfTheSeven();
await switchPartyIfNeeded(settings.partyName)
@@ -39,17 +69,70 @@ async function FeatherFailing() {
let result = captureGameRegion().find(tpIconRo);
if (result.isExist()) {
log.info("传送点图标已识别,点击传送");
result.click();
await sleep(1000);
if (settings.autoPortableWaypoint) {
let result = captureGameRegion().find(ocrRo);
log.info("识别到锚点文字: " + result.text);
// 使用正则表达式匹配不同的时间格式
const dayHourPattern = /(\d+)天(\d+)小时/; // X天X小时格式
const hourOnlyPattern = /(\d+)小时/; // X小时格式
let totalHours = 0;
let dayMatch = result.text.match(dayHourPattern);
let hourMatch = result.text.match(hourOnlyPattern);
if (dayMatch) {
// 匹配到 X天X小时 格式
const days = parseInt(dayMatch[1]);
const hours = parseInt(dayMatch[2]);
totalHours = days * 24 + hours;
log.info(`解析到剩余时间: ${days}${hours}小时,总计${totalHours}小时`);
} else if (hourMatch) {
// 匹配到 X小时 格式不足24小时
totalHours = parseInt(hourMatch[1]);
log.info(`解析到剩余时间: ${totalHours}小时`);
} else {
log.warn("无法解析锚点剩余时间格式,本次不放置锚点");
}
// 判断是否需要放置锚点
if (totalHours < settings.autoPortableWaypointHours) {
await PlacePortableWaypoint();
} else {
log.info(`剩余时间${totalHours}小时,大于设定阈值${settings.autoPortableWaypointHours}小时,不放置锚点`);
}
}
result = captureGameRegion().find(goTeleportRo);
result.click();
await sleep(1000);
await genshin.returnMainUi();
await sleep(1000);
} else {
throw new Error("传送点图标未识别,没有放置传送点");
log.warn("传送点图标未识别,没有放置传送点");
await genshin.tp(3347.59, 2756.12);
if (settings.keymousePartyName) {
await sleep(500);
if (!await switchPartyIfNeeded(settings.keymousePartyName)) {
throw new Error("未找到队伍 " + settings.keymousePartyName + ",请检查设置");
}
} else {
throw new Error("请在设置中配置1号位放凯亚4号位放七七或多莉无任何移速加成的队伍");
}
await sleep(500);
await keyPress("VK_4");
await sleep(500);
await keyMouseScript.runFile("assets/keymouse/锚点放置处.json");
await sleep(500);
await switchPartyIfNeeded(settings.partyName);
if (settings.autoPortableWaypoint) {
await PlacePortableWaypoint();
}
}
result.click();
await sleep(1000);
result = captureGameRegion().find(goTeleportRo);
result.click();
await sleep(1000);
await genshin.returnMainUi();
await sleep(1000);
for (let i = 0; i < 20; i++) {
keyDown("W");
await sleep(50);
@@ -57,7 +140,7 @@ async function FeatherFailing() {
await sleep(300);
result = captureGameRegion().find(swimStateRo);
if (result.isExist()) {
log.info("已进入游泳状态");
log.info("已进入游泳状态");
break;
}
}
@@ -75,7 +158,7 @@ async function FeatherFailing() {
await sleep(25000);
keyUp("VK_LCONTROL");
log.info("我已无敌!");
if(settings.Test){
await pathingScript.runFile("assets/pathing/信仰之跃.json");
}
if (settings.Test) {
await pathingScript.runFile("assets/pathing/信仰之跃.json");
}
}

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "摔落保护:提瓦特反牛顿机制",
"version": "1.0",
"version": "1.1",
"description": "利用游戏机制获取免疫摔落伤害的buff",
"authors": [
{

View File

@@ -1,12 +1,30 @@
[
{
"name": "keymousePartyName",
"type": "input-text",
"label": "用于卡进锚点位置的队伍\n1号位放凯亚4号位放七七或多莉\n无任何移速加成的队伍"
},
{
"name": "partyName",
"type": "input-text",
"label": "需要摔落保护的队伍名称"
},
{
"name": "autoPortableWaypoint",
"type": "checkbox",
"label": "是否自动放置传送锚点",
"default": false
},
{
"name": "autoPortableWaypointHours",
"type": "input-text",
"label": "剩余多少时间自动放置传送锚点(单位:小时)",
"default": 24
},
{
"name": "test",
"type": "checkbox",
"label": "是否想看看效果"
"label": "测试一下免疫摔落的效果",
"default": false
}
]