From 8785c9d00a610bac2f3e51bd60f318cf971392b3 Mon Sep 17 00:00:00 2001 From: Status102 <102887808+status102@users.noreply.github.com> Date: Tue, 10 Feb 2026 23:50:32 +0800 Subject: [PATCH] =?UTF-8?q?rft:=20=E8=BF=87=E6=9C=9F=E5=85=B3=E5=8D=A1?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=88=A0=E9=99=A4=E7=BA=BF=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=20(#15657)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * rft: 使用`不修改关卡`替代 显式修改为`无效关卡` * fix: 过滤过期关卡 * feat: 过期关卡增加删除线 * perf: 移除不必要的警告 * perf: 简化过滤 * fix: 无法隐藏关卡, 取消手动输入关卡名后验证关卡名有效性 --------- Co-authored-by: uye <99072975+ABA2396@users.noreply.github.com> --- .../FightTaskStageResetModeConverter.cs | 6 +- ...kStageResetModeInvalidToIgnoreConverter.cs | 133 ++++++++++++++++++ .../Configuration/Factory/ConfigFactory.cs | 2 +- .../Constants/Enums/FightStageResetMode.cs | 4 +- src/MaaWpfGui/Helper/ConfigConverter.cs | 2 +- src/MaaWpfGui/Main/AsstProxy.cs | 2 +- src/MaaWpfGui/Res/Localizations/en-us.xaml | 2 +- src/MaaWpfGui/Res/Localizations/ja-jp.xaml | 2 +- src/MaaWpfGui/Res/Localizations/ko-kr.xaml | 2 +- src/MaaWpfGui/Res/Localizations/zh-cn.xaml | 2 +- src/MaaWpfGui/Res/Localizations/zh-tw.xaml | 2 +- .../FightSettingsUserControlModel.cs | 81 +++++++---- .../TaskQueue/MallSettingsUserControlModel.cs | 2 +- .../TaskQueue/FightSettingsUserControl.xaml | 31 ++-- 14 files changed, 222 insertions(+), 51 deletions(-) create mode 100644 src/MaaWpfGui/Configuration/Converter/FightTaskStageResetModeInvalidToIgnoreConverter.cs diff --git a/src/MaaWpfGui/Configuration/Converter/FightTaskStageResetModeConverter.cs b/src/MaaWpfGui/Configuration/Converter/FightTaskStageResetModeConverter.cs index 6b7c6858ac..30cd61f232 100644 --- a/src/MaaWpfGui/Configuration/Converter/FightTaskStageResetModeConverter.cs +++ b/src/MaaWpfGui/Configuration/Converter/FightTaskStageResetModeConverter.cs @@ -57,13 +57,13 @@ internal class FightTaskStageResetModeConverter : JsonConverter { if (!taskElement.TryGetProperty("StageResetMode", out _)) { - if (fightTask.UseOptionalStage) + if (fightTask.HideUnavailableStage) { - fightTask.StageResetMode = FightStageResetMode.Invalid; + fightTask.StageResetMode = FightStageResetMode.Current; } else { - fightTask.StageResetMode = FightStageResetMode.Current; + fightTask.StageResetMode = FightStageResetMode.Ignore; } } } diff --git a/src/MaaWpfGui/Configuration/Converter/FightTaskStageResetModeInvalidToIgnoreConverter.cs b/src/MaaWpfGui/Configuration/Converter/FightTaskStageResetModeInvalidToIgnoreConverter.cs new file mode 100644 index 0000000000..7f5f31453d --- /dev/null +++ b/src/MaaWpfGui/Configuration/Converter/FightTaskStageResetModeInvalidToIgnoreConverter.cs @@ -0,0 +1,133 @@ +// +// Part of the MaaWpfGui project, maintained by the MaaAssistantArknights team (Maa Team) +// Copyright (C) 2021-2025 MaaAssistantArknights Contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License v3.0 only as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY +// +#nullable enable +using System; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace MaaWpfGui.Configuration.Converter; + +internal class FightTaskStageResetModeInvalidToIgnoreConverter : JsonConverter +{ + private const string TypeDiscriminatorProperty = "$type"; + private const string FightTaskTypeName = "FightTask"; + private const string StageResetModeProperty = "StageResetMode"; + private const string InvalidValue = "Invalid"; + private const string IgnoreValue = "Ignore"; + + public override Root? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("Expected StartObject token"); + } + + using var jsonDoc = JsonDocument.ParseValue(ref reader); + JsonNode? rootNode = JsonNode.Parse(jsonDoc.RootElement.GetRawText()); + + if (rootNode is JsonObject rootObj) + { + ReplaceInvalidStageResetModeInTaskQueues(rootObj); + } + + string modifiedJson = rootNode?.ToJsonString() ?? string.Empty; + return JsonSerializer.Deserialize(modifiedJson, GetOptionsWithoutThisConverter(options)); + } + + /// + /// 遍历 Configurations.*.TaskQueue,将 $type 为 FightTask 且 StageResetMode 为 "Invalid" 的项改为 "Ignore" + /// + private static void ReplaceInvalidStageResetModeInTaskQueues(JsonObject root) + { + if (root["Configurations"] is not JsonObject configurations) + { + return; + } + + foreach (var (_, configNode) in configurations) + { + if (configNode is not JsonObject config) + { + continue; + } + + if (config["TaskQueue"] is not JsonArray taskQueue) + { + continue; + } + + foreach (var taskNode in taskQueue) + { + if (taskNode is not JsonObject task) + { + continue; + } + + if (!IsFightTask(task)) + { + continue; + } + + if (task[StageResetModeProperty] is JsonValue modeNode && + modeNode.GetValue() == InvalidValue) + { + task[StageResetModeProperty] = IgnoreValue; + } + + if (task["StagePlan"] is JsonArray stagePlan) + { + for (int i = 0; i < stagePlan.Count; i++) + { + if (stagePlan[i] is JsonValue stageNode && + stageNode.GetValue() == "__INVALID__") + { + stagePlan[i] = "OR-8"; + } + } + } + } + } + } + + /// + /// 判断任务节点是否为 FightTask(依据 $type 字段) + /// + private static bool IsFightTask(JsonObject task) + { + if (task[TypeDiscriminatorProperty] is not JsonValue typeNode) + { + return false; + } + + return typeNode.GetValue() == FightTaskTypeName; + } + + public override void Write(Utf8JsonWriter writer, Root value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, GetOptionsWithoutThisConverter(options)); + } + + private static JsonSerializerOptions GetOptionsWithoutThisConverter(JsonSerializerOptions options) + { + var newOptions = new JsonSerializerOptions(options); + for (int i = newOptions.Converters.Count - 1; i >= 0; i--) + { + if (newOptions.Converters[i] is FightTaskStageResetModeInvalidToIgnoreConverter) + { + newOptions.Converters.RemoveAt(i); + } + } + return newOptions; + } +} diff --git a/src/MaaWpfGui/Configuration/Factory/ConfigFactory.cs b/src/MaaWpfGui/Configuration/Factory/ConfigFactory.cs index 9fb5ea643a..6bcaecc99a 100644 --- a/src/MaaWpfGui/Configuration/Factory/ConfigFactory.cs +++ b/src/MaaWpfGui/Configuration/Factory/ConfigFactory.cs @@ -61,7 +61,7 @@ public static class ConfigFactory // ReSharper disable once EventNeverSubscribedTo.Global public static event ConfigurationUpdateEventHandler? ConfigurationUpdateEvent; - private static readonly JsonSerializerOptions _options = new() { WriteIndented = true, Converters = { new JsonStringEnumConverter(), new FightTaskStageResetModeConverter() }, Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; + private static readonly JsonSerializerOptions _options = new() { WriteIndented = true, Converters = { new JsonStringEnumConverter(), new FightTaskStageResetModeInvalidToIgnoreConverter (), new FightTaskStageResetModeConverter() }, Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; // TODO: 参考 ConfigurationHelper ,拆几个函数出来 private static readonly Lazy _rootConfig = new(() => { diff --git a/src/MaaWpfGui/Constants/Enums/FightStageResetMode.cs b/src/MaaWpfGui/Constants/Enums/FightStageResetMode.cs index 4484c4588f..3af57e5f99 100644 --- a/src/MaaWpfGui/Constants/Enums/FightStageResetMode.cs +++ b/src/MaaWpfGui/Constants/Enums/FightStageResetMode.cs @@ -21,7 +21,7 @@ public enum FightStageResetMode Current, /// - /// 无效关卡 + /// 忽略 / 不变 /// - Invalid, + Ignore, } diff --git a/src/MaaWpfGui/Helper/ConfigConverter.cs b/src/MaaWpfGui/Helper/ConfigConverter.cs index 76a8da8c97..8bbe95cbb9 100644 --- a/src/MaaWpfGui/Helper/ConfigConverter.cs +++ b/src/MaaWpfGui/Helper/ConfigConverter.cs @@ -212,7 +212,7 @@ public class ConfigConverter ConfigurationHelper.DeleteValue(ConfigurationKeys.HideUnavailableStage); ConfigurationHelper.DeleteValue(ConfigurationKeys.CustomStageCode); ConfigurationHelper.DeleteValue(ConfigurationKeys.UseAlternateStage); - fightTask.StageResetMode = fightTask.UseOptionalStage ? FightStageResetMode.Invalid : FightStageResetMode.Current; + fightTask.StageResetMode = fightTask.HideUnavailableStage ? FightStageResetMode.Current : FightStageResetMode.Ignore; if (fightTask.Series > 6) { diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs index 59f61cf6ab..88183bd6b2 100644 --- a/src/MaaWpfGui/Main/AsstProxy.cs +++ b/src/MaaWpfGui/Main/AsstProxy.cs @@ -1426,7 +1426,7 @@ public class AsstProxy && (Instances.TaskQueueViewModel.TaskItemViewModels.FirstOrDefault(t => t.TaskId == taskId)?.Index ?? -1) is int index and > -1 && index <= ConfigFactory.CurrentConfig.TaskQueue.Count && ConfigFactory.CurrentConfig.TaskQueue[index] is Configuration.Single.MaaTask.FightTask fight - && FightTask.GetFightStage(fight.StagePlan) is "Annihilation") + && FightTask.GetFightStage(fight) is "Annihilation") { Instances.TaskQueueViewModel.AddLog("AnnihilationStage, " + LocalizationHelper.GetString("GiveUpUploadingPenguins")); break; diff --git a/src/MaaWpfGui/Res/Localizations/en-us.xaml b/src/MaaWpfGui/Res/Localizations/en-us.xaml index 475323a2ae..940d59d9a0 100644 --- a/src/MaaWpfGui/Res/Localizations/en-us.xaml +++ b/src/MaaWpfGui/Res/Localizations/en-us.xaml @@ -748,7 +748,7 @@ Do not adjust the proxy multiplier setting in the game Cur/Last - Invalid + The event is not open Unsupported stages Old Version, Update required diff --git a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml index 5261e8635d..0783248a4e 100644 --- a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml +++ b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml @@ -749,7 +749,7 @@ C:\\leidian\\LDPlayer9 現在/前回 - 無効なレベル + ステージが未公開か既に終了している 未サポートステージ バージョンの更新が必要 diff --git a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml index 518ac79e38..807ea898f1 100644 --- a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml +++ b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml @@ -750,7 +750,7 @@ C:\\leidian\\LDPlayer9 현재/최근 - 잘못된 스테이지 + 미개방 스테이지 미지원 스테이지 낮은 버전 diff --git a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml index 031c206fcc..504cfa252d 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml @@ -749,7 +749,7 @@ C:\\leidian\\LDPlayer9。\n 当前/上次 - 无效关卡 + 活动未开放 不支持的关卡 版本过低 diff --git a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml index c1d9858377..fbeb7e717e 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml @@ -749,7 +749,7 @@ C:\\leidian\\LDPlayer9\n 目前/上次 - 無效關卡 + 活動未開放 不支援的關卡 版本過低 diff --git a/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/FightSettingsUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/FightSettingsUserControlModel.cs index a80f9b2519..131b2224c1 100644 --- a/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/FightSettingsUserControlModel.cs +++ b/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/FightSettingsUserControlModel.cs @@ -92,7 +92,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel { "炭", "SK-5" }, }; - private readonly StageSourceItem InvalidStage = new() { Display = LocalizationHelper.GetString("InvalidStage"), Value = "__INVALID__", IsOpen = false, IsVisible = false }; + /* private readonly StageSourceItem InvalidStage = new() { Display = LocalizationHelper.GetString("InvalidStage"), Value = "__INVALID__", IsOpen = false, IsVisible = false };*/ public ObservableCollection StagePlan { get => field; set => SetAndNotify(ref field, value); } = []; @@ -151,7 +151,23 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel public bool CustomStageCode { get => GetTaskConfig().IsStageManually; - set => SetTaskConfig(t => t.IsStageManually == value, t => t.IsStageManually = value); + set { + bool ret = SetTaskConfig(t => t.IsStageManually == value, t => t.IsStageManually = value); + if (ret && !value) + { + var stagePlan = GetTaskConfig().StagePlan; + for (int i = 0; i < stagePlan.Count; i++) + { + var stage = stagePlan[i]; + if (!Instances.StageManager.GetStageList().Any(p => p.Value == stage)) + { + stagePlan[i] = string.Empty; + } + } + SetTaskConfig(t => t.StagePlan.SequenceEqual(stagePlan), t => t.StagePlan = stagePlan); + RefreshCurrentStagePlan(); + } + } } /// @@ -490,7 +506,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel if (value) { HideUnavailableStage = false; - StageResetMode = FightStageResetMode.Invalid; + StageResetMode = FightStageResetMode.Ignore; } else { @@ -569,7 +585,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel public List> StageResetModeList { get; } = [ new() { Display = LocalizationHelper.GetString("DefaultStage"), Value = FightStageResetMode.Current }, - new() { Display = LocalizationHelper.GetString("InvalidStage"), Value = FightStageResetMode.Invalid }, + new() { Display = LocalizationHelper.GetString("NotSwitch"), Value = FightStageResetMode.Ignore }, ]; public FightStageResetMode StageResetMode @@ -636,11 +652,16 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel return value; } - public static string? GetFightStage(IEnumerable stageNames) + public static string? GetFightStage(FightTask fightTask) { - var list = stageNames.Where(i => i != Instance.InvalidStage.Value); - var stage = list.FirstOrDefault(Instances.TaskQueueViewModel.IsStageOpen); - stage ??= list.FirstOrDefault(); + if (fightTask == null) + { + return null; + } + + var list = fightTask.StagePlan; + var stage = list?.FirstOrDefault(Instances.TaskQueueViewModel.IsStageOpen); + stage ??= list?.FirstOrDefault(); return stage; } @@ -711,7 +732,6 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel { return SerializeTask(fight, Instances.TaskQueueViewModel.TaskItemViewModels[index].TaskId); } - _logger.Error("Failed to set fight params: current task is not in the task queue."); return null; } @@ -728,7 +748,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel } using var scope = _lock.EnterScope(); - var stage = GetFightStage(fight.StagePlan); + var stage = GetFightStage(fight); if (stage is null) { return null; @@ -792,19 +812,24 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel RefreshStageList(); foreach (var task in ConfigFactory.CurrentConfig.TaskQueue.OfType().Where(i => !i.IsStageManually)) { + var originalPlan = task.StagePlan.ToList(); + bool reset = false; for (int i = 0; i < task.StagePlan.Count; i++) { var stage = task.StagePlan[i]; - if (stage != InvalidStage.Value && !stageList.Any(p => p.Value == stage)) + if (!stageList.Any(p => p.Value == stage)) { - task.StagePlan[i] = task.StageResetMode switch { - FightStageResetMode.Current => string.Empty, - FightStageResetMode.Invalid => InvalidStage.Value, - _ => string.Empty, - }; - _logger.Information("Reset non-existing stage from plan: {Stage} to {}", stage, task.StagePlan[i]); + reset = true; + if (task.StageResetMode == FightStageResetMode.Current) + { + task.StagePlan[i] = string.Empty; + } } } + if (reset) + { + _logger.Information("Reset non-existing stage: {} to {}", string.Join(", ", originalPlan), string.Join(", ", task.StagePlan)); + } } RefreshCurrentStagePlan(); }); @@ -822,11 +847,10 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel var listSource = stageList.Select(i => new StageSourceItem() { Display = i.Display, Value = i.Value, IsVisible = !HideUnavailableStage || i.IsStageOpen(Instances.TaskQueueViewModel.CurDayOfWeek), IsOpen = Instances.StageManager.GetStageList().FirstOrDefault(p => p.Value == i.Value)?.IsStageOpen(Instances.TaskQueueViewModel.CurDayOfWeek) ?? true }).ToList(); // 补过期关卡进来 - foreach (var item in listCurrent.Where(i => i != InvalidStage.Value && !listSource.Any(p => p.Value == i))) + foreach (var item in listCurrent.Where(i => !listSource.Any(p => p.Value == i))) { - listSource.Add(new StageSourceItem() { Display = item, Value = item, IsOpen = false, IsVisible = false }); + listSource.Add(new StageSourceItem() { Display = item, Value = item, IsOpen = false, IsVisible = false, IsOutdated = true }); } - listSource.Add(InvalidStage); // 无效关卡 listSource.FirstOrDefault(i => i.Value == "Annihilation")?.Display = current.UseCustomAnnihilation ? (AnnihilationModeList.FirstOrDefault(i => i.Value == current.AnnihilationStage).Key ?? LocalizationHelper.GetString("Annihilation.Current")) : LocalizationHelper.GetString("Annihilation.Current"); StageListSource = new ObservableCollection(listSource); current.StagePlan = listCurrent; // StageListSource更新后, 恢复StagePlan @@ -935,6 +959,11 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel public bool IsOpen { get => field; set => SetAndNotify(ref field, value); } = true; public bool IsVisible { get => field; set => SetAndNotify(ref field, value); } = true; + + /// + /// Gets or sets a value indicating whether 过期活动关卡, 加删除线 + /// + public bool IsOutdated { get; set; } = false; } public class StagePlanItem(string stage = "") : PropertyChangedBase @@ -958,18 +987,12 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel return; } - if (value == Instance.InvalidStage.Value) - { - IsOpen = false; - } - else - { - IsOpen = Instances.StageManager.GetStageList().FirstOrDefault(p => p.Value == value)?.IsStageOpen(Instances.TaskQueueViewModel.CurDayOfWeek) ?? true; - } + IsOpen = Instances.StageManager.GetStageList().FirstOrDefault(p => p.Value == value)?.IsStageOpen(Instances.TaskQueueViewModel.CurDayOfWeek) ?? true; Instance.SetFightParams(); } } = stage; - public bool IsOpen { get => field; set => SetAndNotify(ref field, value); } = true; + // 仅供 ComboBox本身 和 手写Stage的TextBlock 绑定使用 + public bool IsOpen { get => field; set => SetAndNotify(ref field, value); } = Instances.TaskQueueViewModel.IsStageOpen(stage); } } diff --git a/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/MallSettingsUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/MallSettingsUserControlModel.cs index 3ee9e7b80c..539f2e1d55 100644 --- a/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/MallSettingsUserControlModel.cs +++ b/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/MallSettingsUserControlModel.cs @@ -196,7 +196,7 @@ public class MallSettingsUserControlModel : TaskSettingsViewModel var fightStageEmpty = ConfigFactory.CurrentConfig.TaskQueue .OfType() .Where(task => task.IsEnable is not false) - .Any(task => string.IsNullOrEmpty(FightSettingsUserControlModel.GetFightStage(task.StagePlan))); + .Any(task => string.IsNullOrEmpty(FightSettingsUserControlModel.GetFightStage(task))); var task = new AsstMallTask() { CreditFight = CreditFightTaskEnabled && !fightStageEmpty, FormationIndex = CreditFightSelectFormation, diff --git a/src/MaaWpfGui/Views/UserControl/TaskQueue/FightSettingsUserControl.xaml b/src/MaaWpfGui/Views/UserControl/TaskQueue/FightSettingsUserControl.xaml index 03eeece905..6d7522672a 100644 --- a/src/MaaWpfGui/Views/UserControl/TaskQueue/FightSettingsUserControl.xaml +++ b/src/MaaWpfGui/Views/UserControl/TaskQueue/FightSettingsUserControl.xaml @@ -266,28 +266,43 @@ Margin="-10,0,0,0"> + + + + + + + + + +