// // 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.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Windows; using JetBrains.Annotations; using MaaWpfGui.Configuration.Single.MaaTask; using MaaWpfGui.Constants; using MaaWpfGui.Helper; using MaaWpfGui.Models.AsstTasks; using MaaWpfGui.Services; using MaaWpfGui.Utilities; using MaaWpfGui.Utilities.ValueType; using MaaWpfGui.ViewModels.UI; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Stylet; namespace MaaWpfGui.ViewModels.UserControl.TaskQueue; /// /// 理智作战 /// public class FightSettingsUserControlModel : TaskViewModel { public static FightTimes? FightReport { get; set; } public static SanityInfo? SanityReport { get; set; } static FightSettingsUserControlModel() { Instance = new(); } public static FightSettingsUserControlModel Instance { get; } private ObservableCollection _stageList = []; /// /// Gets or private sets the list of stages. /// public ObservableCollection StageList { get => _stageList; private set => SetAndNotify(ref _stageList, value); } private ObservableCollection _remainingSanityStageList = []; public ObservableCollection RemainingSanityStageList { get => _remainingSanityStageList; private set => SetAndNotify(ref _remainingSanityStageList, value); } /// /// Gets the stage. /// public string Stage { get { Stage1 ??= _stage1Fallback; if (!UseAlternateStage) { return Stage1; } if (Instances.TaskQueueViewModel.IsStageOpen(Stage1)) { return Stage1; } if (Instances.TaskQueueViewModel.IsStageOpen(Stage2 ??= string.Empty)) { return Stage2; } if (Instances.TaskQueueViewModel.IsStageOpen(Stage3 ??= string.Empty)) { return Stage3; } return Instances.TaskQueueViewModel.IsStageOpen(Stage4 ??= string.Empty) ? Stage4 : Stage1; } } private readonly Dictionary _stageDictionary = new() { { "AN", "Annihilation" }, { "剿灭", "Annihilation" }, { "CE", "CE-6" }, { "龙门币", "CE-6" }, { "LS", "LS-6" }, { "经验", "LS-6" }, { "狗粮", "LS-6" }, { "CA", "CA-5" }, { "技能", "CA-5" }, { "AP", "AP-5" }, { "红票", "AP-5" }, { "SK", "SK-5" }, { "碳", "SK-5" }, { "炭", "SK-5" }, }; public string?[] Stages => [Stage1, Stage2, Stage3, Stage4]; // Try to fix: issues#5742. 关卡选择为 null 时的一个补丁,可能是 StageList 改变后,wpf binding 延迟更新的问题。 private string _stage1Fallback = ConfigurationHelper.GetValue(ConfigurationKeys.Stage1, string.Empty) ?? string.Empty; private string? _stage1 = ConfigurationHelper.GetValue(ConfigurationKeys.Stage1, string.Empty) ?? string.Empty; /// /// Gets or sets the stage1. /// public string? Stage1 { get => _stage1; set { if (_stage1 == value) { return; } if (CustomStageCode) { // 从后往前删 if (_stage1?.Length != 3 && value != null) { value = ToUpperAndCheckStage(value); } } SetAndNotify(ref _stage1, value); Instances.TaskQueueViewModel.SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.Stage1, value); Instances.TaskQueueViewModel.UpdateDatePrompt(); } } private string? _stage2 = ConfigurationHelper.GetValue(ConfigurationKeys.Stage2, string.Empty) ?? string.Empty; /// /// Gets or sets the stage2. /// public string? Stage2 { get => _stage2; set { if (_stage2 == value) { return; } if (CustomStageCode) { if (_stage2?.Length != 3 && value != null) { value = ToUpperAndCheckStage(value); } } SetAndNotify(ref _stage2, value); Instances.TaskQueueViewModel.SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.Stage2, value); Instances.TaskQueueViewModel.UpdateDatePrompt(); } } private string? _stage3 = ConfigurationHelper.GetValue(ConfigurationKeys.Stage3, string.Empty) ?? string.Empty; /// /// Gets or sets the stage3. /// public string? Stage3 { get => _stage3; set { if (_stage3 == value) { return; } if (CustomStageCode) { if (_stage3?.Length != 3 && value != null) { value = ToUpperAndCheckStage(value); } } SetAndNotify(ref _stage3, value); Instances.TaskQueueViewModel.SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.Stage3, value); Instances.TaskQueueViewModel.UpdateDatePrompt(); } } private string? _stage4 = ConfigurationHelper.GetValue(ConfigurationKeys.Stage4, string.Empty) ?? string.Empty; /// /// Gets or sets the stage4. /// public string? Stage4 { get => _stage4; set { if (_stage4 == value) { return; } if (CustomStageCode) { if (_stage4?.Length != 3 && value != null) { value = ToUpperAndCheckStage(value); } } SetAndNotify(ref _stage4, value); Instances.TaskQueueViewModel.SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.Stage4, value); Instances.TaskQueueViewModel.UpdateDatePrompt(); } } private bool _useRemainingSanityStage = ConfigurationHelper.GetValue(ConfigurationKeys.UseRemainingSanityStage, true); public bool UseRemainingSanityStage { get => _useRemainingSanityStage; set { SetAndNotify(ref _useRemainingSanityStage, value); ConfigurationHelper.SetValue(ConfigurationKeys.UseRemainingSanityStage, value.ToString()); } } private bool _customStageCode = ConfigurationHelper.GetValue(ConfigurationKeys.CustomStageCode, false); /// /// Gets or sets a value indicating whether to use custom stage code. /// public bool CustomStageCode { get => _customStageCode; set { if (!value) { RemoveNonExistStage(); } SetAndNotify(ref _customStageCode, value); ConfigurationHelper.SetValue(ConfigurationKeys.CustomStageCode, value.ToString()); } } /// /// 移除不在关卡列表中的关卡,关闭自定义和刷新关卡列表时调用 /// public void RemoveNonExistStage() { Stage1 = StageList.FirstOrDefault(x => x.Value == Stage1)?.Value ?? string.Empty; Stage2 = StageList.FirstOrDefault(x => x.Value == Stage2)?.Value ?? string.Empty; Stage3 = StageList.FirstOrDefault(x => x.Value == Stage3)?.Value ?? string.Empty; Stage4 = StageList.FirstOrDefault(x => x.Value == Stage4)?.Value ?? string.Empty; RemainingSanityStage = RemainingSanityStageList.FirstOrDefault(x => x.Value == RemainingSanityStage)?.Value ?? string.Empty; } private string? _remainingSanityStage = ConfigurationHelper.GetValue(ConfigurationKeys.RemainingSanityStage, string.Empty) ?? string.Empty; public string? RemainingSanityStage { get => _remainingSanityStage; set { if (_remainingSanityStage == value) { return; } if (CustomStageCode) { if (_remainingSanityStage?.Length != 3 && value != null) { value = ToUpperAndCheckStage(value); } } SetAndNotify(ref _remainingSanityStage, value); TaskQueueViewModel.SetFightRemainingSanityParams(); ConfigurationHelper.SetValue(ConfigurationKeys.RemainingSanityStage, value); } } /// /// Reset unsaved battle parameters. /// public void ResetFightVariables() { UseStone ??= false; UseMedicine ??= false; HasTimesLimited ??= false; IsSpecifiedDrops ??= false; } private bool? _useMedicine = ConfigurationHelper.GetValue(ConfigurationKeys.UseMedicine, false); /// /// Gets or sets a value indicating whether to use medicine with null. /// public bool? UseMedicine { get => _useMedicine; set { SetAndNotify(ref _useMedicine, value); if (value == false) { UseStoneDisplay = false; } Instances.TaskQueueViewModel.SetFightParams(); value ??= false; ConfigurationHelper.SetValue(ConfigurationKeys.UseMedicine, value.ToString()); } } private int _medicineNumber = ConfigurationHelper.GetValue(ConfigurationKeys.UseMedicineQuantity, 999); /// /// Gets or sets the amount of medicine used. /// public int MedicineNumber { get => _medicineNumber; set { if (!SetAndNotify(ref _medicineNumber, value)) { return; } Instances.TaskQueueViewModel.SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.UseMedicineQuantity, value.ToString()); } } public static string UseStoneString => LocalizationHelper.GetString("UseOriginitePrime"); private bool? _useStone = ConfigurationHelper.GetValue(ConfigurationKeys.UseMedicine, false) && ConfigurationHelper.GetValue(ConfigurationKeys.UseStone, false); /// /// Gets or sets a value indicating whether to use originiums with null. /// public bool? UseStone { get => _useStone; set { if (!AllowUseStoneSave && value == true) { value = null; } SetAndNotify(ref _useStone, value); if (value != false) { MedicineNumber = 999; if (UseMedicine == false) { UseMedicine = value; } } Instances.TaskQueueViewModel.SetFightParams(); if (AllowUseStoneSave) { ConfigurationHelper.SetValue(ConfigurationKeys.UseStone, (value ?? false).ToString()); } } } /// /// Gets or sets a value indicating whether to use originiums. /// // ReSharper disable once MemberCanBePrivate.Global [PropertyDependsOn(nameof(UseStone))] public bool UseStoneDisplay { get => UseStone != false; set => UseStone = value; } private int _stoneNumber = ConfigurationHelper.GetValue(ConfigurationKeys.UseStoneQuantity, 0); /// /// Gets or sets the amount of originiums used. /// public int StoneNumber { get => _stoneNumber; set { if (!SetAndNotify(ref _stoneNumber, value)) { return; } Instances.TaskQueueViewModel.SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.UseStoneQuantity, value.ToString()); } } private bool? _hasTimesLimited = ConfigurationHelper.GetValue(ConfigurationKeys.TimesLimited, false); /// /// Gets or sets a value indicating whether the number of times is limited with null. /// public bool? HasTimesLimited { get => _hasTimesLimited; set { SetAndNotify(ref _hasTimesLimited, value); Instances.TaskQueueViewModel.SetFightParams(); value ??= false; ConfigurationHelper.SetValue(ConfigurationKeys.TimesLimited, value.ToString()); } } private int _maxTimes = ConfigurationHelper.GetValue(ConfigurationKeys.TimesLimitedQuantity, 5); /// /// Gets or sets the max number of times. /// public int MaxTimes { get => _maxTimes; set { if (!SetAndNotify(ref _maxTimes, value)) { return; } Instances.TaskQueueViewModel.SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.TimesLimitedQuantity, value.ToString()); } } public static Dictionary SeriesList { get; } = new() { { "AUTO", 0 }, { "6", 6 }, { "5", 5 }, { "4", 4 }, { "3", 3 }, { "2", 2 }, { "1", 1 }, { LocalizationHelper.GetString("NotSwitch"), -1 }, }; private int _series = InitFightSeries(); private static int InitFightSeries() { var series = ConfigurationHelper.GetValue(ConfigurationKeys.SeriesQuantity, 0); if (SeriesList.ContainsValue(series)) { return series; } ConfigurationHelper.SetValue(ConfigurationKeys.SeriesQuantity, "0"); return 0; } /// /// Gets or sets the max number of times. /// public int Series { get => _series; set { if (!SetAndNotify(ref _series, value)) { return; } Instances.TaskQueueViewModel.SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.SeriesQuantity, value.ToString()); } } #region Drops private bool? _isSpecifiedDrops = ConfigurationHelper.GetValue(ConfigurationKeys.DropsEnable, false); /// /// Gets or sets a value indicating whether the drops are specified. /// public bool? IsSpecifiedDrops { get => _isSpecifiedDrops; set { if (!SetAndNotify(ref _isSpecifiedDrops, value)) { return; } Instances.TaskQueueViewModel.SetFightParams(); value ??= false; ConfigurationHelper.SetValue(ConfigurationKeys.DropsEnable, value.ToString()); } } /// /// Gets the list of all drops. /// private List AllDrops { get; } = []; /// /// 关卡不可掉落的材料 /// private static readonly HashSet _excludedValues = [ "3213", "3223", "3233", "3243", // 双芯片 "3253", "3263", "3273", "3283", // 双芯片 "7001", "7002", "7003", "7004", // 许可 "4004", "4005", // 凭证 "3105", "3131", "3132", "3133", // 龙骨/加固建材 "6001", // 演习券 "3141", "4002", // 源石 "32001", // 芯片助剂 "30115", // 聚合剂 "30125", // 双极纳米片 "30135", // D32钢 "30145", // 晶体电子单元 "30155", // 烧结核凝晶 "30165", // 重相位对映体 ]; public void InitDrops() { AllDrops.Add(new() { Display = LocalizationHelper.GetString("NotSelected"), Value = string.Empty }); foreach (var (val, value) in ItemListHelper.ArkItems) { // 不是数字的东西都是正常关卡不会掉的(大概吧) if (!int.TryParse(val, out _)) { continue; } var dis = value.Name; if (_excludedValues.Contains(val)) { continue; } AllDrops.Add(new() { Display = dis, Value = val }); } AllDrops.Sort((a, b) => string.Compare(a.Value, b.Value, StringComparison.Ordinal)); DropsList = [.. AllDrops]; if (AllDrops.FirstOrDefault(i => i.Value == DropsItemId) is { } item) { DropsItemName = item.Display; NotifyOfPropertyChange(nameof(DropsItemName)); } else { DropsItemId = string.Empty; DropsItemName = string.Empty; NotifyOfPropertyChange(nameof(DropsItemName)); } } /// /// Gets or private sets the list of drops. /// public ObservableCollection DropsList { get; private set; } = []; private string _dropsItemId = ConfigurationHelper.GetValue(ConfigurationKeys.DropsItemId, string.Empty) ?? string.Empty; /// /// Gets or sets the item ID of drops. /// public string DropsItemId { get => _dropsItemId; set { SetAndNotify(ref _dropsItemId, value); Instances.TaskQueueViewModel.SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.DropsItemId, DropsItemId); } } /// /// Gets or sets the item Name of drops. /// public string DropsItemName { get; set; } = string.Empty; // UI 绑定的方法 [UsedImplicitly] public void DropsListDropDownClosed() { if (DropsList.FirstOrDefault(i => i.Display == DropsItemName) is { } item) { DropsItemId = item.Value; } else { DropsItemId = string.Empty; DropsItemName = LocalizationHelper.GetString("NotSelected"); NotifyOfPropertyChange(nameof(DropsItemName)); } } private int _dropsQuantity = ConfigurationHelper.GetValue(ConfigurationKeys.DropsQuantity, 5); /// /// Gets or sets the quantity of drops. /// public int DropsQuantity { get => _dropsQuantity; set { SetAndNotify(ref _dropsQuantity, value); Instances.TaskQueueViewModel.SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.DropsQuantity, value.ToString()); } } #endregion Drops public static Dictionary AnnihilationModeList { get; } = new() { { LocalizationHelper.GetString("Annihilation"), "Annihilation" }, { LocalizationHelper.GetString("Chernobog"), "Chernobog@Annihilation" }, { LocalizationHelper.GetString("LungmenOutskirts"), "LungmenOutskirts@Annihilation" }, { LocalizationHelper.GetString("LungmenDowntown"), "LungmenDowntown@Annihilation" }, }; private bool _useCustomAnnihilation = ConfigurationHelper.GetValue(ConfigurationKeys.UseCustomAnnihilation, false); public bool UseCustomAnnihilation { get => _useCustomAnnihilation; set { SetAndNotify(ref _useCustomAnnihilation, value); ConfigurationHelper.SetValue(ConfigurationKeys.UseCustomAnnihilation, value.ToString()); } } private string _annihilationStage = ConfigurationHelper.GetValue(ConfigurationKeys.AnnihilationStage, "Annihilation"); public string AnnihilationStage { get => _annihilationStage; set { SetAndNotify(ref _annihilationStage, value); ConfigurationHelper.SetValue(ConfigurationKeys.AnnihilationStage, value); } } private bool _isDrGrandet = ConfigurationHelper.GetValue(ConfigurationKeys.IsDrGrandet, false); /// /// Gets or sets a value indicating whether to use DrGrandet mode. /// public bool IsDrGrandet { get => _isDrGrandet; set { SetAndNotify(ref _isDrGrandet, value); ConfigurationHelper.SetValue(ConfigurationKeys.IsDrGrandet, value.ToString()); } } private bool _useAlternateStage = ConfigurationHelper.GetValue(ConfigurationKeys.UseAlternateStage, false); /// /// Gets or sets a value indicating whether to use alternate stage. /// public bool UseAlternateStage { get => _useAlternateStage; set { SetAndNotify(ref _useAlternateStage, value); ConfigurationHelper.SetValue(ConfigurationKeys.UseAlternateStage, value.ToString()); if (value) { HideUnavailableStage = false; } } } private bool _allowUseStoneSave = ConfigurationHelper.GetValue(ConfigurationKeys.AllowUseStoneSave, false); public bool AllowUseStoneSave { get => _allowUseStoneSave; set { if (value) { var result = MessageBoxHelper.Show( LocalizationHelper.GetString("AllowUseStoneSaveWarning"), LocalizationHelper.GetString("Warning"), MessageBoxButton.YesNo, MessageBoxImage.Warning, no: LocalizationHelper.GetString("Confirm"), yes: LocalizationHelper.GetString("Cancel"), iconBrushKey: "DangerBrush"); if (result != MessageBoxResult.No) { return; } } SetAndNotify(ref _allowUseStoneSave, value); ConfigurationHelper.SetValue(ConfigurationKeys.AllowUseStoneSave, value.ToString()); } } private bool _useExpiringMedicine = ConfigurationHelper.GetValue(ConfigurationKeys.UseExpiringMedicine, false); public bool UseExpiringMedicine { get => _useExpiringMedicine; set { SetAndNotify(ref _useExpiringMedicine, value); ConfigurationHelper.SetValue(ConfigurationKeys.UseExpiringMedicine, value.ToString()); Instances.TaskQueueViewModel.SetFightParams(); } } private bool _hideUnavailableStage = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.HideUnavailableStage, bool.TrueString)); /// /// Gets or sets a value indicating whether to hide unavailable stages. /// public bool HideUnavailableStage { get => _hideUnavailableStage; set { SetAndNotify(ref _hideUnavailableStage, value); ConfigurationHelper.SetValue(ConfigurationKeys.HideUnavailableStage, value.ToString()); if (value) { UseAlternateStage = false; } UpdateStageList(); } } private bool _hideSeries = ConfigurationHelper.GetValue(ConfigurationKeys.HideSeries, false); /// /// Gets or sets a value indicating whether to hide series. /// public bool HideSeries { get => _hideSeries; set { SetAndNotify(ref _hideSeries, value); ConfigurationHelper.SetValue(ConfigurationKeys.HideSeries, value.ToString()); } } private bool _autoRestartOnDrop = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.AutoRestartOnDrop, bool.TrueString)); public bool AutoRestartOnDrop { get => _autoRestartOnDrop; set { SetAndNotify(ref _autoRestartOnDrop, value); ConfigurationHelper.SetValue(ConfigurationKeys.AutoRestartOnDrop, value.ToString()); } } private string ToUpperAndCheckStage(string value) { if (string.IsNullOrEmpty(value)) { return value; } string upperValue = value.ToUpper(); if (_stageDictionary.TryGetValue(upperValue, out var stage)) { return stage; } if (StageList == null) { return value; } foreach (var item in StageList) { if (upperValue == item.Value.ToUpper() || upperValue == item.Display.ToUpper()) { return item.Value; } } return value; } public override (AsstTaskType Type, JObject Params) Serialize() { var task = new AsstFightTask() { Stage = Stage, Medicine = UseMedicine != false ? MedicineNumber : 0, Stone = UseStoneDisplay ? StoneNumber : 0, Series = Series, MaxTimes = HasTimesLimited != false ? MaxTimes : int.MaxValue, ExpiringMedicine = UseExpiringMedicine ? 9999 : 0, IsDrGrandet = IsDrGrandet, ReportToPenguin = SettingsViewModel.GameSettings.EnablePenguin, ReportToYituliu = SettingsViewModel.GameSettings.EnableYituliu, PenguinId = SettingsViewModel.GameSettings.PenguinId, YituliuId = SettingsViewModel.GameSettings.PenguinId, ServerType = Instances.SettingsViewModel.ServerType, ClientType = SettingsViewModel.GameSettings.ClientType, }; if (Stage == "Annihilation" && UseCustomAnnihilation) { task.Stage = AnnihilationStage; } if (IsSpecifiedDrops != false && !string.IsNullOrEmpty(DropsItemId)) { task.Drops.Add(DropsItemId, DropsQuantity); } if(HasTimesLimited is not false && Series > 0 && MaxTimes % Series != 0) { Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetStringFormat("FightTimesMayNotExhausted", MaxTimes, Series), UiLogColor.Warning); } return task.Serialize(); } #region 双入口设置可见性 private bool _customInfrastPlanShowInFightSettings = ConfigurationHelper.GetValue(ConfigurationKeys.CustomInfrastPlanShowInFightSettings, false); public bool CustomInfrastPlanShowInFightSettings { get => _customInfrastPlanShowInFightSettings; set { SetAndNotify(ref _customInfrastPlanShowInFightSettings, value); ConfigurationHelper.SetValue(ConfigurationKeys.CustomInfrastPlanShowInFightSettings, value.ToString()); } } #endregion #region 关卡列表更新 /// /// Updates stage list. /// 使用手动输入时,只更新关卡列表,不更新关卡选择 /// 使用隐藏当日不开放时,更新关卡列表,关卡选择为未开放的关卡时清空 /// 使用备选关卡时,更新关卡列表,关卡选择为未开放的关卡时在关卡列表中添加对应未开放关卡,避免清空导致进入上次关卡 /// 啥都不选时,更新关卡列表,关卡选择为未开放的关卡时在关卡列表中添加对应未开放关卡,避免清空导致进入上次关卡 /// 除手动输入外所有情况下,如果剩余理智为未开放的关卡,会被清空 /// // FIXME: 被注入对象只能在private函数内使用,只有Model显示之后才会被注入。如果Model还没有触发OnInitialActivate时调用函数会NullPointerException // 这个函数被列为public可见,意味着他注入对象前被调用 public void UpdateStageList() { Execute.PostToUIThreadAsync(() => { var hideUnavailableStage = HideUnavailableStage; Instances.TaskQueueViewModel.EnableSetFightParams = false; var stage1 = Stage1 ?? string.Empty; var stage2 = Stage2 ?? string.Empty; var stage3 = Stage3 ?? string.Empty; var stage4 = Stage4 ?? string.Empty; var rss = RemainingSanityStage ?? string.Empty; var tempStageList = hideUnavailableStage ? Instances.StageManager.GetStageList(Instances.TaskQueueViewModel.CurDayOfWeek).ToList() : Instances.StageManager.GetStageList().ToList(); var tempRemainingSanityStageList = Instances.StageManager.GetStageList().ToList(); if (CustomStageCode) { // 7% // 使用自定义的时候不做处理 } else if (hideUnavailableStage) { // 15% stage1 = Instances.TaskQueueViewModel.GetValidStage(stage1); stage2 = Instances.TaskQueueViewModel.GetValidStage(stage2); stage3 = Instances.TaskQueueViewModel.GetValidStage(stage3); stage4 = Instances.TaskQueueViewModel.GetValidStage(stage4); } else if (UseAlternateStage) { // 11% AddStagesIfNotExist([stage1, stage2, stage3, stage4], tempStageList); } else { // 啥都没选 AddStageIfNotExist(stage1, tempStageList); // 避免关闭了使用备用关卡后,始终添加备用关卡中的未开放关卡 stage2 = Instances.TaskQueueViewModel.GetValidStage(stage2); stage3 = Instances.TaskQueueViewModel.GetValidStage(stage3); stage4 = Instances.TaskQueueViewModel.GetValidStage(stage4); } // rss 如果结束后还选择了不开放的关卡,理智作战任务会报错 rss = Instances.TaskQueueViewModel.IsStageOpen(rss) ? rss : string.Empty; if (tempRemainingSanityStageList.Any(item => item.Value == string.Empty)) { var itemToRemove = tempRemainingSanityStageList.First(item => item.Value == string.Empty); tempRemainingSanityStageList.Remove(itemToRemove); } tempRemainingSanityStageList.Insert(0, new CombinedData { Display = LocalizationHelper.GetString("NoUse"), Value = string.Empty }); UpdateObservableCollection(StageList, tempStageList); UpdateObservableCollection(RemainingSanityStageList, tempRemainingSanityStageList); _stage1Fallback = stage1; Stage1 = stage1; Stage2 = stage2; Stage3 = stage3; Stage4 = stage4; RemainingSanityStage = rss; if (!CustomStageCode) { RemoveNonExistStage(); } Instances.TaskQueueViewModel.EnableSetFightParams = true; }); } private void AddStagesIfNotExist(IEnumerable stages, List stageList) { foreach (var stage in stages) { AddStageIfNotExist(stage, stageList); } } private void AddStageIfNotExist(string stage, List stageList) { if (stageList.Any(x => x.Value == stage)) { return; } var stageInfo = Instances.StageManager.GetStageInfo(stage); stageList.Add(stageInfo); } /// /// 更新 ObservableCollection,确保不替换原集合,而是增删项 /// /// 原始 ObservableCollection /// 新的列表 public static void UpdateObservableCollection(ObservableCollection originalCollection, List newList) { originalCollection.Clear(); foreach (var item in newList) { originalCollection.Add(item); } } #endregion 关卡列表更新 public class SanityInfo { [JsonProperty("current_sanity")] public int SanityCurrent { get; set; } [JsonProperty("max_sanity")] public int SanityMax { get; set; } [JsonProperty("report_time")] public DateTimeOffset ReportTime { get; set; } } public class FightTimes { [JsonProperty("sanity_cost")] public int SanityCost { get; set; } [JsonProperty("series")] public int Series { get; set; } [JsonProperty("times_finished")] public int TimesFinished { get; set; } [JsonProperty("finished")] public bool IsFinished { get; set; } } }