Merge branch 'main' into main

This commit is contained in:
mfkvfhpdx
2025-01-10 10:29:53 +08:00
committed by GitHub
18 changed files with 136 additions and 111 deletions

View File

@@ -68,10 +68,14 @@ public partial class PathingPartyConfig : ObservableObject
// 启用进入剧情自动脱离
[ObservableProperty]
private bool _autoSkipEnabled = true;
// 自动冲刺启用
[ObservableProperty]
private bool _autoRunEnabled = true;
//启用自动战斗配置
[ObservableProperty]
private bool _autoFightEabled = false;
private bool _autoFightEnabled = false;
[ObservableProperty]
private AutoFightConfig _autoFightConfig = new();

View File

@@ -189,6 +189,7 @@ public class AutoDomainTask : ISoloTask
if (gameScreenSize.Width * 9 != gameScreenSize.Height * 16)
{
Logger.LogError("游戏窗口分辨率不是 16:9 !当前分辨率为 {Width}x{Height} , 非 16:9 分辨率的游戏无法正常使用自动秘境功能 !", gameScreenSize.Width, gameScreenSize.Height);
throw new Exception("游戏窗口分辨率不是 16:9");
}
if (gameScreenSize.Width < 1920 || gameScreenSize.Height < 1080)

View File

@@ -42,22 +42,24 @@ public class AutoFightTask : ISoloTask
private class TaskFightFinishDetectConfig
{
public int DelayTime=1500;
public int DelayTime = 1500;
public Dictionary<string, int> DelayTimes = new();
public double CheckTime = 5;
public List<string> CheckNames = new();
public bool FastCheckEnabled;
public TaskFightFinishDetectConfig(AutoFightParam.FightFinishDetectConfig finishDetectConfig)
{
FastCheckEnabled=finishDetectConfig.FastCheckEnabled;
ParseCheckTimeString(finishDetectConfig.FastCheckParams,out CheckTime,CheckNames);
ParseFastCheckEndDelayString(finishDetectConfig.CheckEndDelay,out DelayTime,DelayTimes);
FastCheckEnabled = finishDetectConfig.FastCheckEnabled;
ParseCheckTimeString(finishDetectConfig.FastCheckParams, out CheckTime, CheckNames);
ParseFastCheckEndDelayString(finishDetectConfig.CheckEndDelay, out DelayTime, DelayTimes);
BattleEndProgressBarColor = ParseStringToTuple(finishDetectConfig.BattleEndProgressBarColor, (95, 235, 255));
BattleEndProgressBarColorTolerance = ParseSingleOrCommaSeparated(finishDetectConfig.BattleEndProgressBarColorTolerance, (6, 6, 6));
}
public (int, int, int) BattleEndProgressBarColor{ get; }
public (int, int, int) BattleEndProgressBarColorTolerance{ get; }
public (int, int, int) BattleEndProgressBarColor { get; }
public (int, int, int) BattleEndProgressBarColorTolerance { get; }
public static void ParseCheckTimeString(
string input,
out double checkTime,
@@ -68,6 +70,7 @@ public class AutoFightTask : ISoloTask
{
return; // 直接返回
}
var uniqueNames = new HashSet<string>(); // 用于临时去重的集合
// 按分号分割字符串
@@ -90,6 +93,7 @@ public class AutoFightTask : ISoloTask
names.AddRange(uniqueNames); // 将集合转换为列表
}
public static void ParseFastCheckEndDelayString(
string input,
out int delayTime,
@@ -99,9 +103,9 @@ public class AutoFightTask : ISoloTask
if (string.IsNullOrEmpty(input))
{
return; // 直接返回
}
// 分割字符串,以分号为分隔符
var segments = input.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
@@ -130,7 +134,7 @@ public class AutoFightTask : ISoloTask
}
}
static bool IsSingleNumber(string input, out int result)
{
return int.TryParse(input, out result);
@@ -162,10 +166,10 @@ public class AutoFightTask : ISoloTask
// 如果解析失败,返回默认值
return defaultValue;
}
}
private TaskFightFinishDetectConfig _finishDetectConfig;
public AutoFightTask(AutoFightParam taskParam)
{
_taskParam = taskParam;
@@ -176,11 +180,11 @@ public class AutoFightTask : ISoloTask
_predictor = BgiYoloV8PredictorFactory.GetPredictor(@"Assets\Model\World\bgi_world.onnx");
}
_finishDetectConfig=new TaskFightFinishDetectConfig(_taskParam.FinishDetectConfig);
_finishDetectConfig = new TaskFightFinishDetectConfig(_taskParam.FinishDetectConfig);
}
// 方法1判断是否是单个数字
/*public int delayTime=1500;
public Dictionary<string, int> delayTimes = new();
public double checkTime = 5;
@@ -205,11 +209,11 @@ public class AutoFightTask : ISoloTask
combatScenes.BeforeTask(cts2.Token);
TimeSpan fightTimeout = TimeSpan.FromSeconds(_taskParam.Timeout); // 战斗超时时间
Stopwatch timeoutStopwatch = Stopwatch.StartNew();
Stopwatch checkFightFinishStopwatch = Stopwatch.StartNew();
TimeSpan checkFightFinishTime = TimeSpan.FromSeconds(_finishDetectConfig.CheckTime); //检查战斗超时时间的超时时间
//战斗前检查,可做成配置
/* if (await CheckFightFinish()) {
return;
@@ -225,7 +229,6 @@ public class AutoFightTask : ISoloTask
{
while (!cts2.Token.IsCancellationRequested)
{
// 通用化战斗策略
for (var i = 0; i < combatCommands.Count; i++)
{
@@ -245,18 +248,16 @@ public class AutoFightTask : ISoloTask
}
lastFighttName = command.Name;
if (!fightEndFlag && _taskParam is { FightFinishDetectEnabled: true } )
if (!fightEndFlag && _taskParam is { FightFinishDetectEnabled: true })
{
//处于最后一个位置,或者当前执行人和下一个人名字不一样的情况,满足一定条件(开启快速检查并且检查时间大于0或人名存在配置)检查战斗
if (i==combatCommands.Count - 1
if (i == combatCommands.Count - 1
|| (
_finishDetectConfig.FastCheckEnabled && command.Name!=combatCommands[i+1].Name &&
((_finishDetectConfig.CheckTime>0 && checkFightFinishStopwatch.Elapsed>checkFightFinishTime)
|| _finishDetectConfig.CheckNames.Contains(command.Name))
))
_finishDetectConfig.FastCheckEnabled && command.Name != combatCommands[i + 1].Name &&
((_finishDetectConfig.CheckTime > 0 && checkFightFinishStopwatch.Elapsed > checkFightFinishTime)
|| _finishDetectConfig.CheckNames.Contains(command.Name))
))
{
checkFightFinishStopwatch.Restart();
var delayTime = _finishDetectConfig.DelayTime;
if (_finishDetectConfig.DelayTimes.TryGetValue(command.Name, out var time))
@@ -276,12 +277,11 @@ public class AutoFightTask : ISoloTask
fightEndFlag = await CheckFightFinish(delayTime);
}
}
if (fightEndFlag)
{
break;
}
}
@@ -341,29 +341,26 @@ public class AutoFightTask : ISoloTask
//当万叶cd大于3时或战斗人次少于2时通常无怪物情况下此时不再触发万叶拾取
if (!(countFight < 2 || lastFighttName== "枫原万叶" && time.TotalSeconds>3))
{
Logger.LogInformation("使用枫原万叶长E拾取掉落物");
await Delay(300, ct);
if (kazuha.TrySwitch())
{
if (time.TotalMilliseconds > 0 && time.TotalSeconds <= kazuha.SkillHoldCd)
{
Logger.LogInformation("枫原万叶长E技能可能处于冷却中等待 {Time} s",time.TotalSeconds);
await Delay((int)Math.Ceiling(time.TotalMilliseconds), ct);
}
kazuha.UseSkill(true);
await Task.Delay(100);
Simulation.SendInput.SimulateAction(GIActions.NormalAttack);
await Delay(1500, ct);
if (time.TotalMilliseconds > 0 && time.TotalSeconds <= kazuha.SkillHoldCd)
{
Logger.LogInformation("枫原万叶长E技能可能处于冷却中等待 {Time} s", time.TotalSeconds);
await Delay((int)Math.Ceiling(time.TotalMilliseconds), ct);
}
kazuha.UseSkill(true);
await Task.Delay(100);
Simulation.SendInput.SimulateAction(GIActions.NormalAttack);
await Delay(1500, ct);
}
}
else
{
Logger.LogInformation((countFight < 2 ? "首个人出招就结束战斗,应该无怪物":"距最近一次万叶出招,时间过短")+",跳过此次万叶拾取!");
}
}
}
@@ -379,7 +376,8 @@ public class AutoFightTask : ISoloTask
var gameScreenSize = SystemControl.GetGameScreenRect(TaskContext.Instance().GameHandle);
if (gameScreenSize.Width * 9 != gameScreenSize.Height * 16)
{
Logger.LogWarning("游戏窗口分辨率不是 16:9 !当前分辨率为 {Width}x{Height} , 非 16:9 分辨率的游戏可能无法正常使用自动战斗功能 !", gameScreenSize.Width, gameScreenSize.Height);
Logger.LogError("游戏窗口分辨率不是 16:9 !当前分辨率为 {Width}x{Height} , 非 16:9 分辨率的游戏无法正常使用自动战斗功能 !", gameScreenSize.Width, gameScreenSize.Height);
throw new Exception("游戏窗口分辨率不是 16:9");
}
}
@@ -391,7 +389,7 @@ public class AutoFightTask : ISoloTask
Math.Abs(a.Item3 - b.Item3) < c.Item3;
}
private async Task<bool> CheckFightFinish(int delayTime=1500)
private async Task<bool> CheckFightFinish(int delayTime = 1500)
{
// YOLO 判断血条和怪物位置
// if (HasFightFlagByYolo(CaptureToRectArea()))
@@ -424,7 +422,7 @@ public class AutoFightTask : ISoloTask
}
}
**/
await Delay(delayTime, _ct);
Logger.LogInformation("打开编队界面检查战斗是否结束");
// 最终方案确认战斗结束

View File

@@ -27,38 +27,41 @@ public class CombatCommand
Method = Method.GetEnumByCode(method);
var parameters = command.Substring(startIndex + 1, endIndex - startIndex - 1);
Args = new List<string>(parameters.Split(',', StringSplitOptions.TrimEntries));
// 校验参数
if (Method == Method.Walk)
{
AssertUtils.IsTrue(Args.Count == 2, "walk方法必须有两个入参第一个参数是方向第二个参数是行走时间。例walk(s, 0.2)");
var s = double.Parse(Args[1]);
AssertUtils.IsTrue(s > 0, "行走时间必须大于0");
}
else if (Method == Method.W || Method == Method.A || Method == Method.S || Method == Method.D)
{
AssertUtils.IsTrue(Args.Count == 1, "w/a/s/d方法必须有一个入参代表行走时间。例d(0.5)");
}
else if (Method == Method.MoveBy)
{
AssertUtils.IsTrue(Args.Count == 2, "moveby方法必须有两个入参分别是x和y。例moveby(100, 100))");
}
else if (Method == Method.KeyDown || Method == Method.KeyUp || Method == Method.KeyPress)
{
AssertUtils.IsTrue(Args.Count == 1, $"{Method.Alias[0]}方法必须有一个入参,代表按键");
try
{
User32Helper.ToVk(Args[0]);
}
catch
{
throw new ArgumentException($"{Method.Alias[0]}方法的入参必须是VirtualKeyCodes枚举中的值当前入参 {Args[0]} 不合法");
}
}
Args = [..parameters.Split(',', StringSplitOptions.TrimEntries)];
}
else
{
Method = Method.GetEnumByCode(command);
Args = [];
}
// 校验参数
if (Method == Method.Walk)
{
AssertUtils.IsTrue(Args.Count == 2, "walk方法必须有两个入参第一个参数是方向第二个参数是行走时间。例walk(s, 0.2)");
var s = double.Parse(Args[1]);
AssertUtils.IsTrue(s > 0, "行走时间必须大于0");
}
else if (Method == Method.W || Method == Method.A || Method == Method.S || Method == Method.D)
{
AssertUtils.IsTrue(Args.Count == 1, "w/a/s/d方法必须有一个入参代表行走时间。例d(0.5)");
}
else if (Method == Method.MoveBy)
{
AssertUtils.IsTrue(Args.Count == 2, "moveby方法必须有两个入参分别是x和y。例moveby(100, 100))");
}
else if (Method == Method.KeyDown || Method == Method.KeyUp || Method == Method.KeyPress)
{
AssertUtils.IsTrue(Args.Count == 1, $"{Method.Alias[0]}方法必须有一个入参,代表按键");
try
{
User32Helper.ToVk(Args[0]);
}
catch
{
throw new ArgumentException($"{Method.Alias[0]}方法的入参必须是VirtualKeyCodes枚举中的值当前入参 {Args[0]} 不合法");
}
}
}

View File

@@ -22,7 +22,7 @@ internal class AutoFightHandler : IActionHandler
TaskControl.Logger.LogInformation("执行 {Text}", "自动战斗");
// 爷们要战斗
AutoFightParam taskParams = null;
if (config != null && config is PathingPartyConfig patyConfig && patyConfig.AutoFightEabled)
if (config != null && config is PathingPartyConfig patyConfig && patyConfig.AutoFightEnabled)
{
//替换配置为路径追踪

View File

@@ -15,7 +15,7 @@ public class CombatScriptHandler : IActionHandler
{
if (waypointForTrack is { CombatScript: not null })
{
Logger.LogInformation("执行 {Text}", "战斗策略脚本");
Logger.LogInformation("执行 {Text}", "简易策略脚本");
var combatScript = waypointForTrack.CombatScript;
var combatScenes = await RunnerContext.Instance.GetCombatScenes(ct);
if (combatScenes == null)
@@ -41,7 +41,7 @@ public class CombatScriptHandler : IActionHandler
}
else
{
Logger.LogError("战斗脚本action_params内容为空");
Logger.LogError("策略脚本action_params内容为空");
}
}

View File

@@ -188,7 +188,10 @@ public class PathExecutor
&& waypoint.Action != ActionEnum.CombatScript.Code
&& waypoint.Action != ActionEnum.Mining.Code))
{
await MoveCloseTo(waypoint);
if (waypoint.Action != ActionEnum.Fight.Code) // 战斗时不需要接近
{
await MoveCloseTo(waypoint);
}
}
//skipOtherOperations如果重试则跳过相关操作
@@ -770,7 +773,7 @@ public class PathExecutor
}
// 自动疾跑
if (distance > 20)
if (distance > 20 && PartyConfig.AutoRunEnabled)
{
if (Math.Abs((fastModeColdTime - now).TotalMilliseconds) > 2500) //冷却时间2.5s,回复体力用
{

View File

@@ -87,7 +87,7 @@ public class TpTask(CancellationToken ct)
await ClickTpPoint(CaptureToRectArea());
// 等待传送完成
for (var i = 0; i < 20; i++)
for (var i = 0; i < 50; i++)
{
await Delay(1200, ct);
using var ra3 = CaptureToRectArea();

View File

@@ -31,6 +31,9 @@ public class GoToAdventurersGuildTask
{
try
{
// 合成完毕后领取奖励
await new ClaimEncounterPointsRewardsTask().Start(ct);
// 如果有好感队伍名称,先切换到好感队伍
if (!string.IsNullOrEmpty(dailyRewardPartyName))
{

View File

@@ -29,8 +29,6 @@ public class GoToCraftingBenchTask
try
{
await DoOnce(country, ct);
// 合成完毕后领取奖励
await new ClaimEncounterPointsRewardsTask().Start(ct);
break;
}
catch (Exception e)
@@ -100,7 +98,8 @@ public class GoToCraftingBenchTask
PartyConfig = new PathingPartyConfig
{
Enabled = true,
AutoSkipEnabled = true
AutoSkipEnabled = true,
AutoRunEnabled = country != "枫丹" ,
},
EndAction = region => Bv.FindFAndPress(region, "合成")
};

View File

@@ -93,7 +93,7 @@ public partial class OneDragonTaskItem : ObservableObject
case "领取每日奖励":
Action = async () =>
{
await new GoToAdventurersGuildTask().Start(config.AdventurersGuildCountry, CancellationContext.Instance.Cts.Token);
await new GoToAdventurersGuildTask().Start(config.AdventurersGuildCountry, CancellationContext.Instance.Cts.Token, config.DailyRewardPartyName);
await new ClaimBattlePassRewardsTask().Start(CancellationContext.Instance.Cts.Token);
};
break;

View File

@@ -172,7 +172,7 @@ public partial class MaskWindow : Window
if (width * 9 != height * 16)
{
_logger.LogWarning("当前游戏分辨率不是16:9部分功能可能无法正常使用");
_logger.LogError("当前游戏分辨率不是16:9一条龙、配队识别、地图传送、地图追踪等所有独立任务与全自动任务相关功能,都将会无法正常使用");
}
// 读取游戏注册表配置

View File

@@ -852,6 +852,32 @@
SelectedItem="{Binding Config.AutoDomainConfig.DomainName, Mode=TwoWay}">
</ComboBox>
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="循环次数"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="循环秘境多少次,输入 0 则为用光所有树脂为止,优先使用浓缩树脂"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="90"
Margin="0,0,36,0"
Text="{Binding AutoDomainRoundNum, Mode=TwoWay}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
@@ -1016,32 +1042,7 @@
Margin="0,0,36,0"
IsChecked="{Binding Config.AutoDomainConfig.AutoEat, Mode=TwoWay}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="循环次数"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="循环秘境多少次,输入 0 则为用光所有树脂为止,优先使用浓缩树脂"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="90"
Margin="0,0,36,0"
Text="{Binding AutoDomainRoundNum, Mode=TwoWay}" />
</Grid>
</StackPanel>
</ui:CardExpander>

View File

@@ -268,7 +268,7 @@
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,24,0"
IsChecked="{Binding PathingConfig.AutoFightEabled, Mode=TwoWay}" />
IsChecked="{Binding PathingConfig.AutoFightEnabled, Mode=TwoWay}" />
<!---->
</Grid>
</ui:CardExpander.Header>

View File

@@ -220,6 +220,12 @@ public partial class HomePageViewModel : ObservableObject, INavigationAware, IVi
private void Start(IntPtr hWnd)
{
if (Config.TriggerInterval <= 0)
{
MessageBox.Error("触发器触发频率必须大于0");
return;
}
if (!TaskDispatcherEnabled)
{
_hWnd = hWnd;

View File

@@ -61,7 +61,7 @@ public partial class OneDragonFlowViewModel : ObservableObject, INavigationAware
private List<string> _adventurersGuildCountry = ["枫丹", "稻妻", "璃月", "蒙德"];
[ObservableProperty]
private List<string> _domainNameList = MapLazyAssets.Instance.DomainNameList;
private List<string> _domainNameList = ["", ..MapLazyAssets.Instance.DomainNameList];
public OneDragonFlowViewModel()
{

View File

@@ -115,7 +115,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
//_combatStrategyList = ["根据队伍自动选择", .. LoadCustomScript(Global.Absolute(@"User\AutoFight"))];
_domainNameList = MapLazyAssets.Instance.DomainNameList;
_domainNameList = ["", ..MapLazyAssets.Instance.DomainNameList];
_autoFightViewModel = new AutoFightViewModel(Config);
}
@@ -245,6 +245,13 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
public bool GetFightStrategy(out string path)
{
if (string.IsNullOrEmpty(Config.AutoFightConfig.StrategyName))
{
Toast.Warning("请先在【独立任务——自动战斗】下拉列表配置中选择战斗策略!");
path = string.Empty;
return true;
}
path = Global.Absolute(@"User\AutoFight\" + Config.AutoFightConfig.StrategyName + ".txt");
if ("根据队伍自动选择".Equals(Config.AutoFightConfig.StrategyName))
{
@@ -253,7 +260,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
if (!File.Exists(path) && !Directory.Exists(path))
{
Toast.Error("战斗策略文件不存在");
Toast.Error("当前选择的自动战斗策略文件不存在");
return true;
}

View File

@@ -21,7 +21,7 @@ internal class WindowsInputMessageDispatcher : IInputMessageDispatcher
if (num != (ulong)(long)inputs.Length)
{
throw new Exception("Some simulated input commands were not sent successfully. The most common reason for this happening are the security features of Windows including User Interface Privacy Isolation (UIPI). Your application can only send commands to applications of the same or lower elevation. Similarly certain commands are restricted to Accessibility/UIAutomation applications. Refer to the project home page and the code samples for more information.");
throw new Exception("模拟键鼠消息发送失败常见原因1.你未以管理员权限运行程序2.存在安全软件拦截比如360");
}
}
}