Files
better-genshin-impact/BetterGenshinImpact/GameTask/AutoFight/OneKeyFightTask.cs
Takaranoao 399441b9e8 优化技能冷却处理逻辑 (#1321)
* 在路径追踪重构了部分冷却处理逻辑,战斗脚本e增加wait参数可等待技能冷却而不是跳过。采矿e增加等待。尝试修复路径追踪 UseElementalSkill 与采矿脚本冲突的问题。

* 给CombatCommand加入快速跳过e的选项

* 优化技能冷却处理逻辑,增加OcrSkillCd属性以支持OCR识别的技能冷却时间,并调整相关技能CD计算和等待逻辑,尝试修复纳西妲采集终止时按键未弹起的问题

* 优化战斗任务中的技能冷却处理逻辑

* 更新纳西妲技能冷却时间记录,改为使用UTC时间并增加日志输出以便调试

* 增加最大技能CD检查,以排除系统时间/日期同步导致无限卡死。修复跑图路切人。(ps:主板电池没电应该去修主板)

* 修复CheckAvatarAvailable

* fix AutoFightTask skill cooldown logic and improve comments

* 尝试修复脚本在"当前角色"下的小问题

* 尝试修复脚本在"当前角色"下的小问题,Avatar类结构调整,重新做了"根据技能cd优化出招"部分。

* Refactor avatar retrieval in PathingConditionConfig to use GetAvatars method and update skill cooldown references

* Fix variable naming for clarity in CombatScenes

* 在自动战斗执行前预先过滤不可执行的脚本。

---------

Co-authored-by: 辉鸭蛋 <huiyadanli@gmail.com>
2025-04-04 13:54:44 +08:00

241 lines
7.1 KiB
C#

using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.GameTask.AutoFight.Model;
using BetterGenshinImpact.GameTask.AutoFight.Script;
using BetterGenshinImpact.Model;
using BetterGenshinImpact.Service;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
namespace BetterGenshinImpact.GameTask.AutoFight;
/// <summary>
/// 一键战斗宏
/// </summary>
public class OneKeyFightTask : Singleton<OneKeyFightTask>
{
public static readonly string HoldOnMode = "按住时重复";
public static readonly string TickMode = "触发";
private Dictionary<string, List<CombatCommand>>? _avatarMacros;
private CancellationTokenSource? _cts = null;
private Task? _fightTask;
private bool _isKeyDown = false;
private int activeMacroPriority = -1;
private DateTime _lastUpdateTime = DateTime.MinValue;
private CombatScenes? _currentCombatScenes;
public void KeyDown()
{
if (_isKeyDown || !IsEnabled())
{
return;
}
_isKeyDown = true;
if (activeMacroPriority != TaskContext.Instance().Config.MacroConfig.CombatMacroPriority ||
IsAvatarMacrosEdited())
{
activeMacroPriority = TaskContext.Instance().Config.MacroConfig.CombatMacroPriority;
_avatarMacros = LoadAvatarMacros();
Logger.LogInformation("加载一键宏配置完成");
}
if (IsHoldOnMode())
{
if (_cts == null || _cts.Token.IsCancellationRequested)
{
_cts = new CancellationTokenSource();
_fightTask = FightTask(_cts.Token);
if (!_fightTask.IsCompleted)
{
_fightTask.Start();
}
}
}
else if (IsTickMode())
{
if (_cts == null || _cts.Token.IsCancellationRequested)
{
_cts = new CancellationTokenSource();
_fightTask = FightTask(_cts.Token);
if (!_fightTask.IsCompleted)
{
_fightTask.Start();
}
}
else
{
_cts.Cancel();
}
}
}
public void KeyUp()
{
_isKeyDown = false;
if (!IsEnabled())
{
return;
}
if (IsHoldOnMode())
{
_cts?.Cancel();
}
}
// public void Run()
// {
// if (!IsEnabled())
// {
// return;
// }
// _avatarMacros ??= LoadAvatarMacros();
//
// if (IsHoldOnMode())
// {
// if (_fightTask == null || _fightTask.IsCompleted)
// {
// _fightTask = FightTask(_cts);
// _fightTask.Start();
// }
// Thread.Sleep(100);
// }
// else if (IsTickMode())
// {
// if (_cts.Token.IsCancellationRequested)
// {
// _cts = new CancellationTokenSource();
// Task.Run(() => FightTask(_cts));
// }
// else
// {
// _cts.Cancel();
// }
// }
// }
/// <summary>
/// 循环执行战斗宏
/// </summary>
private Task FightTask(CancellationToken ct)
{
var imageRegion = CaptureToRectArea();
var combatScenes = new CombatScenes().InitializeTeam(imageRegion);
if (!combatScenes.CheckTeamInitialized())
{
if (_currentCombatScenes == null)
{
Logger.LogError("首次队伍角色识别失败");
return Task.CompletedTask;
}
else
{
Logger.LogWarning("队伍角色识别失败,使用上次识别结果,队伍未切换时无影响");
}
}
else
{
_currentCombatScenes = combatScenes;
}
// 找到出战角色
// var activeAvatar = _currentCombatScenes.GetAvatars().First(avatar => avatar.IsActive(imageRegion));
var avatarName = _currentCombatScenes.CurrentAvatar(true, imageRegion, ct);
if (avatarName is null)
{
Logger.LogError("无法识别出战角色");
return Task.CompletedTask;
}
var activeAvatar = _currentCombatScenes.SelectAvatar(avatarName);
if (activeAvatar is null)
{
Logger.LogError("获取出战角色{Name}失败", avatarName);
return Task.CompletedTask;
}
if (_avatarMacros != null && _avatarMacros.TryGetValue(activeAvatar.Name, out var combatCommands))
{
return new Task(() =>
{
Logger.LogInformation("→ {Name}执行宏", activeAvatar.Name);
while (!ct.IsCancellationRequested && IsEnabled())
{
if (IsHoldOnMode() && !_isKeyDown)
{
break;
}
// 通用化战斗策略
foreach (var command in combatCommands)
{
command.Execute(activeAvatar);
}
}
Logger.LogInformation("→ {Name}停止宏", activeAvatar.Name);
});
}
else
{
Logger.LogWarning("→ {Name}配置[{Priority}]为空,请先配置一键宏", activeAvatar.Name, activeMacroPriority);
return Task.CompletedTask;
}
}
public Dictionary<string, List<CombatCommand>> LoadAvatarMacros()
{
var jsonPath = Global.Absolute("User/avatar_macro.json");
var json = File.ReadAllText(jsonPath);
_lastUpdateTime = File.GetLastWriteTime(jsonPath);
var avatarMacros = JsonSerializer.Deserialize<List<AvatarMacro>>(json, ConfigService.JsonOptions);
if (avatarMacros == null)
{
return [];
}
var result = new Dictionary<string, List<CombatCommand>>();
foreach (var avatarMacro in avatarMacros)
{
var commands = avatarMacro.LoadCommands();
if (commands != null)
{
result.Add(avatarMacro.Name, commands);
}
}
return result;
}
public bool IsAvatarMacrosEdited()
{
// 通过修改时间判断是否编辑过
var jsonPath = Global.Absolute("User/avatar_macro.json");
var lastWriteTime = File.GetLastWriteTime(jsonPath);
return lastWriteTime > _lastUpdateTime;
}
public static bool IsEnabled()
{
return TaskContext.Instance().Config.MacroConfig.CombatMacroEnabled;
}
public static bool IsHoldOnMode()
{
return TaskContext.Instance().Config.MacroConfig.CombatMacroHotkeyMode == HoldOnMode;
}
public static bool IsTickMode()
{
return TaskContext.Instance().Config.MacroConfig.CombatMacroHotkeyMode == TickMode;
}
}