通过点位配置,支持在未识别点位情况下,从大地图中心点来识别坐标。 (#1526)

* 修改调度器任务和部分独立任务失去焦点时,强制切换回游戏窗口,如果用常规的方式无法激活窗口,则第10次会尝试最小化所有窗口后激活游戏。

* 去除未引入的类引用

* 修正战斗结束后,大概率打开队伍界面的问题

* 修复有些电脑上因未知原因,战斗0秒打断

* 把失焦激活放入了设置-通用设置-其他设置中,默认关闭。暂停恢复时,重置移动的起始时间,防止因暂停而导致超时放弃任务。

* 在调度器里面的任务之前,增加月卡处理,解决4点如果未进入任务会卡住的问题。增加了日志分析小怪详细。解决日志分析兜底结束日期不生效的问题。

* 在设置=》其他设置中 增加调度器任务传送过程中自动领取探索奖励功能配置。

* 调整自动派遣后恢复原任务的逻辑

* 自动领取派遣奖励时,跳过异常,防止整个配置组任务被打断。

* 把打开大地图方法从TpTask中抽出为公共方法,自动领取派遣代码调整到了调度器中。

* 去除了未使用的引用

* 暂停恢复逻辑增加恢复中条件和非空判断

* 增加了临时暂停自动拾取的逻辑(RunnerContext.AutoPickTriggerStopCount 为0时不限制,大于0时停止,多次暂停会累加该值,每次恢复-1),支持嵌套情况的暂停,在自动派遣(和结束后5秒)或暂停调度器任务时,同时暂停自动拾取功能。

* 调整暂停拾取方法

* 调整个日志输出

* 路径追踪复苏时,暂停拾取

* 增加根据点位配置,支持能在点位未识别情况下,使用大地图中心点的方式来定位,从而支持像铜锁小岛处这种小地图无法识别的点位。调整了对未识别点位的默认逻辑,未配置点位配置情况下,未识别点位,会取上一个识别的点位,从而支持在某些地方断续小地图能识别情况下的脚本。
This commit is contained in:
mfkvfhpdx
2025-05-11 01:25:08 +08:00
committed by GitHub
parent a5473aa129
commit 040cb5080b
3 changed files with 173 additions and 12 deletions

View File

@@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using BetterGenshinImpact.GameTask.AutoPathing.Model.Enum;
namespace BetterGenshinImpact.GameTask.AutoPathing.Model;
@@ -6,8 +9,35 @@ namespace BetterGenshinImpact.GameTask.AutoPathing.Model;
[Serializable]
public class Waypoint
{
//异常识别处理
public class Misidentification
{
//何时处理 pathTooFar 路径过远 Unrecognized 未识别
public List<string> Type { get; set; }= ["unrecognized"];
//处理方式 previousDetectedPoint 取上一个识别到的点位置 , mapRecognition 大地图识别 , ScheduledArrival 特定时间到达
public string HandlingMode { get; set; } = "previousDetectedPoint";
//自行预估的时间
public int ArrivalTime { get; set; } = 0;
}
public class ExtParams
{
public Misidentification Misidentification { get; set; } = new();
public string Description { get; set; } = "";
//normal 小怪,elite 精英,legendary 传奇
public string MonsterTag { get; set; }
}
public double X { get; set; }
public double Y { get; set; }
public ExtParams PointExtParams { get; set; }=new ExtParams();
/// <summary>
/// <see cref="WaypointType"/>

View File

@@ -20,6 +20,8 @@ public class WaypointForTrack : Waypoint
public double MatY { get; set; }
public string MapName { get; set; }
//异常识别
public Misidentification Misidentification { get; set; } = new();
/// <summary>
/// 存在 combat_script 的 action 的话,这个值会存在

View File

@@ -492,7 +492,12 @@ public class PathExecutor
private List<List<WaypointForTrack>> ConvertWaypointsForTrack(List<Waypoint> positions, PathingTask task)
{
// 把 X Y 转换为 MatX MatY
var allList = positions.Select(waypoint => new WaypointForTrack(waypoint, task.Info.MapName)).ToList();
var allList = positions.Select(waypoint =>
{
WaypointForTrack wft=new WaypointForTrack(waypoint, task.Info.MapName);
wft.Misidentification=waypoint.PointExtParams.Misidentification;
return wft;
}).ToList();
// 按照WaypointType.Teleport.Code切割数组
var result = new List<List<WaypointForTrack>>();
@@ -676,7 +681,7 @@ public class PathExecutor
public async Task FaceTo(WaypointForTrack waypoint)
{
var screen = CaptureToRectArea();
var position = await GetPosition(screen, waypoint.MapName);
var position = await GetPosition(screen, waypoint);
var targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
Logger.LogInformation("朝向点,位置({x2},{y2})", $"{waypoint.GameX:F1}", $"{waypoint.GameY:F1}");
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 2);
@@ -691,7 +696,7 @@ public class PathExecutor
await SwitchAvatar(PartyConfig.MainAvatarIndex);
var screen = CaptureToRectArea();
var position = await GetPosition(screen, waypoint.MapName);
var (position, additionalTimeInMs) = await GetPositionAndTime(screen, waypoint);
var targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
Logger.LogInformation("粗略接近途经点,位置({x2},{y2})", $"{waypoint.GameX:F1}", $"{waypoint.GameY:F1}");
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 5);
@@ -722,7 +727,17 @@ public class PathExecutor
EndJudgment(screen);
position = await GetPosition(screen, waypoint.MapName);
// position = await GetPosition(screen, waypoint);
(position, additionalTimeInMs) = await GetPositionAndTime(screen, waypoint);
if (additionalTimeInMs>0)
{
if (!Simulation.IsKeyDown(GIActions.MoveForward.ToActionKey().ToVK()))
{
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
}
additionalTimeInMs = additionalTimeInMs + 1000;//当做起步补偿
}
var distance = Navigation.GetDistance(waypoint, position);
Debug.WriteLine($"接近目标点中,距离为{distance}");
if (distance < 4)
@@ -739,7 +754,7 @@ public class PathExecutor
}
else
{
Logger.LogWarning("距离过远,跳过路径点");
Logger.LogWarning($"距离过远{position.X},{position.Y}->{waypoint.X},{waypoint.Y}={distance},跳过路径点");
}
@@ -749,7 +764,7 @@ public class PathExecutor
// 非攀爬状态下,检测是否卡死(脱困触发器)
if (waypoint.MoveMode != MoveModeEnum.Climb.Code)
{
if ((DateTime.UtcNow - lastPositionRecord).TotalMilliseconds > 1000)
if ((DateTime.UtcNow - lastPositionRecord).TotalMilliseconds > 1000 + additionalTimeInMs)
{
lastPositionRecord = DateTime.UtcNow;
prevPositions.Add(position);
@@ -943,7 +958,7 @@ public class PathExecutor
EndJudgment(screen);
position = await GetPosition(screen, waypoint.MapName);
position = await GetPosition(screen, waypoint);
if (Navigation.GetDistance(waypoint, position) < 2)
{
Logger.LogInformation("已到达路径点");
@@ -980,7 +995,7 @@ public class PathExecutor
{
Simulation.SendInput.Mouse.MiddleButtonClick();
var screen = CaptureToRectArea();
var position = await GetPosition(screen, waypoint.MapName);
var position = await GetPosition(screen, waypoint);
var targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 10);
var handler = ActionFactory.GetBeforeHandler(waypoint.Action);
@@ -1039,11 +1054,74 @@ public class PathExecutor
Logger.LogInformation("尝试切换角色{Name}失败!", avatar.Name);
return null;
}
private async Task<Point2f> GetPosition(ImageRegion imageRegion, string mapName)
/// <summary>
/// 根据时间在两个点之间插值。
/// </summary>
/// <param name="startPoint">起点坐标</param>
/// <param name="endPoint">终点坐标</param>
/// <param name="startTime">起始时间</param>
/// <param name="midTime">中间时间</param>
/// <param name="endTime">结束时间</param>
/// <returns>中间点坐标</returns>
public static Point2f InterpolatePointByTime(
Point2f startPoint,
Point2f endPoint,
DateTime startTime,
DateTime midTime,
DateTime endTime)
{
var position = Navigation.GetPosition(imageRegion, mapName);
// 计算时间差
double totalMillis = (endTime - startTime).TotalMilliseconds;
double midMillis = (midTime - startTime).TotalMilliseconds;
// 防止除以0
if (totalMillis == 0)
return startPoint;
// 计算比例
float t = (float)(midMillis / totalMillis);
if (t>1.0f)
{
t = 1.0f;
}
// 插值计算
float x = startPoint.X + (endPoint.X - startPoint.X) * t;
float y = startPoint.Y + (endPoint.Y - startPoint.Y) * t;
return new Point2f(x, y);
}
private Point2f prePosition;
private DateTime preTime;
//自动构造点位的最大时间
private int maxAutoPositionTime=10000;
private async Task WaitForCloseMap(int maxAttempts, int delayMs)
{
await Delay(delayMs, ct);
for (var i = 0; i < maxAttempts; i++)
{
using var capture = CaptureToRectArea();
if (Bv.IsInMainUi(capture))
{
return;
}
await Delay(delayMs, ct);
}
}
private async Task<Point2f> GetPosition(ImageRegion imageRegion, WaypointForTrack waypoint)
{
return (await GetPositionAndTime(imageRegion, waypoint)).point;
}
private async Task<(Point2f point,int additionalTimeInMs)> GetPositionAndTime(ImageRegion imageRegion, WaypointForTrack waypoint)
{
var position = Navigation.GetPosition(imageRegion, waypoint.MapName);
int time = 0;
if (position == new Point2f())
{
if (!Bv.IsInMainUi(imageRegion))
@@ -1053,7 +1131,58 @@ public class PathExecutor
}
}
return position;
var distance = Navigation.GetDistance(waypoint, position);
//何时处理 pathTooFar 路径过远 unrecognized 未识别
if ((position is {X:0,Y:0} && waypoint.Misidentification.Type.Contains("unrecognized")) || (distance>500 && waypoint.Misidentification.Type.Contains("pathTooFar")))
{
if (waypoint.Misidentification.HandlingMode == "previousDetectedPoint")
{
if (prePosition != default)
{
position = prePosition;
Logger.LogInformation(@$"未识别到具体路径,取上次点位");
}
}else if (waypoint.Misidentification.HandlingMode == "mapRecognition"){
//大地图识别坐标
DateTime start = DateTime.Now;
TpTask tpTask = new TpTask(ct);
await tpTask.OpenBigMapUi();
try
{
position =MapManager.GetMap(waypoint.MapName).ConvertGenshinMapCoordinatesToImageCoordinates(tpTask.GetPositionFromBigMap(waypoint.MapName));
}
catch (Exception e)
{
Logger.LogInformation(@$"地图中心点识别失败!");
}
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_ESCAPE);
//Bv.IsInMainUi(imageRegion);
await WaitForCloseMap(10,200);
DateTime end = DateTime.Now;
time=(int)(end - start).TotalMilliseconds;
Logger.LogInformation(@$"未识别到具体路径,打开地图计算中心点({position.X},{position.Y})");
}
/*if (prePosition!=default)
{*/
//position = InterpolatePointByTime(prePosition,new Point2f((float)waypoint.GameX,(float)waypoint.GameY),preTime,DateTime.Now,preTime.AddMilliseconds(maxAutoPositionTime));
//Logger.LogInformation(@$"未识别到具体路径,预测其路径为({position.X},{position.Y},开始结束点位为:({prePosition.X},{prePosition.Y}{waypoint.GameX},{waypoint.GameY}");
//Point2f GetBigMapCenterPoint(string mapName)
// Logger.LogInformation(@$"未识别到具体路径,打开地图计算中心点({position.X},{position.Y})");
//position =prePosition;
// }
}
else
{
prePosition = position;
preTime = DateTime.Now;
}
//Logger.LogInformation("识别到路径:"+position.X+","+position.Y);
return (position,time);
}
/**