// // MaaWpfGui - A part of the MaaCoreArknights project // Copyright (C) 2021 MistEO and Contributors // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License 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 // using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; using MaaWpfGui.Constants; using MaaWpfGui.Extensions; using MaaWpfGui.Helper; using MaaWpfGui.Main; using MaaWpfGui.Models; using MaaWpfGui.Services; using MaaWpfGui.Utilities.ValueType; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Serilog; using StyletIoC; using Application = System.Windows.Application; using ComboBox = System.Windows.Controls.ComboBox; using Screen = Stylet.Screen; namespace MaaWpfGui.ViewModels.UI { /// /// The view model of task queue. /// public class TaskQueueViewModel : Screen { private readonly IContainer _container; private StageManager _stageManager; private static readonly ILogger _logger = Log.ForContext(); /// /// Gets or sets the view models of task items. /// public ObservableCollection TaskItemViewModels { get; set; } /// /// Gets the visibility of task setting views. /// public TaskSettingVisibilityInfo TaskSettingVisibilities { get; } = TaskSettingVisibilityInfo.Current; public SettingsViewModel TaskSettingDataContext { get => Instances.SettingsViewModel; } /// /// 实时更新任务顺序 /// public void TaskItemSelectionChanged() { int index = 0; foreach (var item in TaskItemViewModels) { ConfigurationHelper.SetTaskOrder(item.OriginalName, index.ToString()); ++index; } } /// /// Gets or sets the view models of log items. /// public ObservableCollection LogItemViewModels { get; set; } private string _actionAfterCompleted = ConfigurationHelper.GetValue(ConfigurationKeys.ActionAfterCompleted, ActionType.DoNothing.ToString()); /// /// Gets or sets the list of the actions after completion. /// public List> ActionAfterCompletedList { get; set; } /// /// Gets or sets the action after completion. /// public ActionType ActionAfterCompleted { get { if (!Enum.TryParse(_actionAfterCompleted, out ActionType action)) { return ActionType.DoNothing; } return action; } set { string storeValue = value.ToString(); SetAndNotify(ref _actionAfterCompleted, storeValue); if (value == ActionType.HibernateWithoutPersist || value == ActionType.ExitEmulatorAndSelfAndHibernateWithoutPersist || value == ActionType.ShutdownWithoutPersist) { storeValue = ActionType.DoNothing.ToString(); } ConfigurationHelper.SetValue(ConfigurationKeys.ActionAfterCompleted, storeValue); } } /// /// Initializes a new instance of the class. /// /// The IoC container. public TaskQueueViewModel(IContainer container) { _container = container; } protected override void OnInitialActivate() { base.OnInitialActivate(); _stageManager = _container.Get(); DisplayName = LocalizationHelper.GetString("Farming"); LogItemViewModels = new ObservableCollection(); InitializeItems(); InitTimer(); } /* public void ShowButton() { Visible = Visibility.Visible; Hibernate = true; } private Visibility _visible = Visibility.Collapsed; public Visibility Visible { get => _visible; set => SetAndNotify(ref _visible, value); } */ public bool Running { get; set; } private readonly DispatcherTimer _timer = new DispatcherTimer(); public bool ConfirmExit() { if (Application.Current.IsShuttingDown()) { // allow close if application is shutting down return true; } if (!Running) { // no need to confirm if no running task return true; } var window = Instances.MainWindowManager.GetWindowIfVisible(); var result = MessageBoxHelper.ShowNative(window, LocalizationHelper.GetString("ConfirmExitText"), LocalizationHelper.GetString("ConfirmExitTitle"), "MAA", MessageBoxButton.YesNo, MessageBoxImage.Information, MessageBoxResult.No); return result == MessageBoxResult.Yes; } #pragma warning disable CS0672 // Member overrides obsolete member // WHY OBSOLETED? protected override bool CanClose() #pragma warning restore CS0672 // Member overrides obsolete member { return ConfirmExit(); } private void InitTimer() { _timer.Interval = TimeSpan.FromSeconds(50); _timer.Tick += Timer1_Elapsed; _timer.Start(); } private void Timer1_Elapsed(object sender, EventArgs e) { if (NeedToUpdateDatePrompt()) { UpdateDatePrompt(); UpdateStageList(false); // 随机延迟,防止同时更新 var delayTime = new Random().Next(0, 10 * 60 * 1000); _ = Task.Run(async () => { await Task.Delay(delayTime); await _stageManager.UpdateStageWeb(); UpdateDatePrompt(); UpdateStageList(false); }); } refreshCustomInfrastPlanIndexByPeriod(); if (!Idle) { return; } if (NeedToCheckForUpdates()) { if (Instances.SettingsViewModel.UpdatAutoCheck) { // 随机延迟,防止同时更新 var delayTime = new Random().Next(0, 10 * 60 * 1000); _ = Task.Run(async () => { await Task.Delay(delayTime); await Instances.SettingsViewModel.ManualUpdate(); }); } } int intMinute = DateTime.Now.Minute; int intHour = DateTime.Now.Hour; var timeToStart = false; for (int i = 0; i < 8; ++i) { if (Instances.SettingsViewModel.TimerModels.Timers[i].IsOn && Instances.SettingsViewModel.TimerModels.Timers[i].Hour == intHour && Instances.SettingsViewModel.TimerModels.Timers[i].Min == intMinute) { timeToStart = true; break; } } if (timeToStart) { LinkStart(); } } /// /// Initializes items. /// private void InitializeItems() { string[] task_list = new string[] { "WakeUp", "Recruiting", "Base", "Combat", "Mall", "Mission", "AutoRoguelike", // "ReclamationAlgorithm", }; ActionAfterCompletedList = new List> { new GenericCombinedData { Display = LocalizationHelper.GetString("DoNothing"), Value = ActionType.DoNothing }, new GenericCombinedData { Display = LocalizationHelper.GetString("ExitArknights"), Value = ActionType.StopGame }, new GenericCombinedData { Display = LocalizationHelper.GetString("ExitEmulator"), Value = ActionType.ExitEmulator }, new GenericCombinedData { Display = LocalizationHelper.GetString("ExitSelf"), Value = ActionType.ExitSelf }, new GenericCombinedData { Display = LocalizationHelper.GetString("ExitEmulatorAndSelf"), Value = ActionType.ExitEmulatorAndSelf }, // new GenericCombData{ Display="待机",Value=ActionTypeAfterCompleted.Suspend }, new GenericCombinedData { Display = LocalizationHelper.GetString("ExitEmulatorAndSelfAndHibernate"), Value = ActionType.ExitEmulatorAndSelfAndHibernate }, new GenericCombinedData { Display = LocalizationHelper.GetString("Hibernate"), Value = ActionType.Hibernate }, new GenericCombinedData { Display = LocalizationHelper.GetString("Shutdown"), Value = ActionType.Shutdown }, // new GenericCombData { Display = Localization.GetString("ExitEmulatorAndSelfAndHibernate") + "*", Value = ActionType.ExitEmulatorAndSelfAndHibernateWithoutPersist }, new GenericCombinedData { Display = LocalizationHelper.GetString("Hibernate") + "*", Value = ActionType.HibernateWithoutPersist }, new GenericCombinedData { Display = LocalizationHelper.GetString("Shutdown") + "*", Value = ActionType.ShutdownWithoutPersist }, }; var temp_order_list = new List(new DragItemViewModel[task_list.Length]); var non_order_list = new List(); for (int i = 0; i != task_list.Length; ++i) { var task = task_list[i]; bool parsed = int.TryParse(ConfigurationHelper.GetTaskOrder(task, "-1"), out var order); var vm = new DragItemViewModel(LocalizationHelper.GetString(task), task, "TaskQueue."); if (task == TaskSettingVisibilityInfo.DefaultVisibleTaskSetting) { vm.EnableSetting = true; } if (!parsed || order < 0 || order >= temp_order_list.Count) { non_order_list.Add(vm); } else { temp_order_list[order] = vm; } } foreach (var new_vm in non_order_list) { int i = 0; while (temp_order_list[i] != null) { ++i; } temp_order_list[i] = new_vm; } TaskItemViewModels = new ObservableCollection(temp_order_list); RemainingSanityStageList = new ObservableCollection(_stageManager.GetStageList()) { // It's Cur/Last option [0] = new CombinedData { Display = LocalizationHelper.GetString("NoUse"), Value = string.Empty }, }; InitDrops(); NeedToUpdateDatePrompt(); UpdateDatePrompt(); UpdateStageList(true); RefreshCustonInfrastPlan(); if (DateTime.UtcNow.ToYJDate().IsAprilFoolsDay()) { AddLog(LocalizationHelper.GetString("BuyWineOnAprilFoolsDay"), UiLogColor.Info); } } private DayOfWeek _curDayOfWeek; /// /// Determine whether the specified stage is open /// /// stage name /// Whether the specified stage is open private bool IsStageOpen(string name) { return _stageManager.IsStageOpen(name, _curDayOfWeek); } /// /// Updates stage list. /// /// Whether or not to update the stage list for selection forcely // FIXME: 被注入对象只能在private函数内使用,只有Model显示之后才会被注入。如果Model还没有触发OnInitialActivate时调用函数会NullPointerException // 这个函数被列为public可见,意味着他注入对象前被调用 public void UpdateStageList(bool forceUpdate) { if (Instances.SettingsViewModel.HideUnavailableStage) { // update available stage list var stage1 = Stage1 ??= string.Empty; StageList = new ObservableCollection(_stageManager.GetStageList(_curDayOfWeek)); // reset closed stage1 to "Last/Current" if (!CustomStageCode) { Stage1 = _stageManager.IsStageOpen(stage1, _curDayOfWeek) ? stage1 : string.Empty; } } else { // initializing or settings changing, update stage list forcely if (forceUpdate) { var stage1 = Stage1 ??= string.Empty; var stage2 = Stage2 ??= string.Empty; var stage3 = Stage3 ??= string.Empty; EnableSetFightParams = false; StageList = new ObservableCollection(_stageManager.GetStageList()); // reset closed stages to "Last/Current" if (!CustomStageCode) { Stage1 = StageList.Any(x => x.Value == stage1) ? stage1 : string.Empty; Stage2 = StageList.Any(x => x.Value == stage2) ? stage2 : string.Empty; Stage3 = StageList.Any(x => x.Value == stage3) ? stage3 : string.Empty; } EnableSetFightParams = true; } } var rss = RemainingSanityStage ??= string.Empty; RemainingSanityStageList = new ObservableCollection(_stageManager.GetStageList()) { [0] = new CombinedData { Display = LocalizationHelper.GetString("NoUse"), Value = string.Empty }, }; if (!CustomStageCode) { RemainingSanityStage = RemainingSanityStageList.Any(x => x.Value == rss) ? rss : string.Empty; } } private bool NeedToUpdateDatePrompt() { var now = DateTime.UtcNow.ToYJDateTime(); var hour = now.Hour; var min = now.Minute; // yj历的4/16点 if (min == 0 && hour == 12) { return true; } else { if (_curDayOfWeek == now.DayOfWeek) { return false; } else { _curDayOfWeek = now.DayOfWeek; return true; } } } private bool NeedToCheckForUpdates() { var now = DateTime.UtcNow.ToYJDateTime(); var hour = now.Hour; var min = now.Minute; // yj历的4/22点 if (min == 0 && hour == 18) { return true; } return false; } /// /// Updates date prompt. /// // FIXME: 被注入对象只能在private函数内使用,只有Model显示之后才会被注入。如果Model还没有触发OnInitialActivate时调用函数会NullPointerException // 这个函数被列为public可见,意味着他注入对象前被调用 public void UpdateDatePrompt() { var builder = new StringBuilder(LocalizationHelper.GetString("TodaysStageTip") + "\n"); // Closed activity stages var stages = new[] { Stage1, Stage2, Stage3 }; foreach (var stage in stages) { if (stage == null) { continue; } if (_stageManager.GetStageInfo(stage)?.IsActivityClosed() == true) { builder.Append(stage).Append(": ").AppendLine(LocalizationHelper.GetString("ClosedStage")); break; } } // Open stages today var openStages = _stageManager.GetStageTips(_curDayOfWeek); if (!string.IsNullOrEmpty(openStages)) { builder.Append(openStages); } var prompt = builder.ToString(); if (StagesOfToday == prompt) { return; } StagesOfToday = prompt; } private string _stagesOfToday = string.Empty; /// /// Gets or sets the stages of today. /// public string StagesOfToday { get => _stagesOfToday; set => SetAndNotify(ref _stagesOfToday, value); } /// /// Adds log. /// /// The content. /// The font color. /// The font weight. public void AddLog(string content, string color = UiLogColor.Trace, string weight = "Regular") { var log = new LogItemViewModel(content, color, weight); LogItemViewModels.Add(log); _logger.Information(content); } /// /// Clears log. /// public void ClearLog() { LogItemViewModels.Clear(); _logger.Information("Main windows log clear."); _logger.Information(string.Empty); } /// /// Selects all. /// public void SelectedAll() { foreach (var item in TaskItemViewModels) { switch (item.OriginalName) { case "AutoRoguelike": case "ReclamationAlgorithm": continue; } item.IsChecked = true; } } private bool _inverseMode = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.MainFunctionInverseMode, bool.FalseString)); /// /// Gets or sets a value indicating whether to use inverse mode. /// public bool InverseMode { get => _inverseMode; set { SetAndNotify(ref _inverseMode, value); InverseShowText = value ? LocalizationHelper.GetString("Inverse") : LocalizationHelper.GetString("Clear"); InverseMenuText = value ? LocalizationHelper.GetString("Clear") : LocalizationHelper.GetString("Inverse"); ConfigurationHelper.SetValue(ConfigurationKeys.MainFunctionInverseMode, value.ToString()); } } /// /// The width of "Select All" when both. /// public const int SelectedAllWidthWhenBoth = 80; private int _selectedAllWidth = ConfigurationHelper.GetValue(ConfigurationKeys.InverseClearMode, "Clear") == "ClearInverse" ? SelectedAllWidthWhenBoth : 85; /// /// Gets or sets the width of "Select All". /// public int SelectedAllWidth { get => _selectedAllWidth; set => SetAndNotify(ref _selectedAllWidth, value); } private int _inverseSelectedWidth = 90; /// /// Gets or sets the width of "Select inversely". /// public int InverseSelectedWidth { get => _inverseSelectedWidth; set => SetAndNotify(ref _inverseSelectedWidth, value); } private bool _showInverse = ConfigurationHelper.GetValue(ConfigurationKeys.InverseClearMode, "Clear") == "ClearInverse"; /// /// Gets or sets a value indicating whether "Select inversely" is visible. /// public bool ShowInverse { get => _showInverse; set => SetAndNotify(ref _showInverse, value); } private string _inverseShowText = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.MainFunctionInverseMode, bool.FalseString)) ? LocalizationHelper.GetString("Inverse") : LocalizationHelper.GetString("Clear"); /// /// Gets or sets the text to be displayed for "Select inversely". /// public string InverseShowText { get => _inverseShowText; set => SetAndNotify(ref _inverseShowText, value); } private string _inverseMenuText = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.MainFunctionInverseMode, bool.FalseString)) ? LocalizationHelper.GetString("Clear") : LocalizationHelper.GetString("Inverse"); /// /// Gets or sets the text of inversion menu. /// public string InverseMenuText { get => _inverseMenuText; set => SetAndNotify(ref _inverseMenuText, value); } /// /// Changes inversion mode. /// public void ChangeInverseMode() { InverseMode = !InverseMode; } /// /// Selects inversely. /// public void InverseSelected() { if (_inverseMode) { foreach (var item in TaskItemViewModels) { switch (item.OriginalName) { case "AutoRoguelike": item.IsChecked = false; continue; case "ReclamationAlgorithm": item.IsChecked = false; continue; } item.IsChecked = !item.IsChecked; } } else { foreach (var item in TaskItemViewModels) { item.IsChecked = false; } } } /// /// Starts. /// public async void LinkStart() { if (Idle == false) { return; } Idle = false; // 虽然更改时已经保存过了,不过保险起见还是在点击开始之后再保存一次任务及基建列表 TaskItemSelectionChanged(); Instances.SettingsViewModel.InfrastOrderSelectionChanged(); ClearLog(); await Task.Run(() => Instances.SettingsViewModel.RunScript("StartsWithScript")); AddLog(LocalizationHelper.GetString("ConnectingToEmulator")); string errMsg = string.Empty; bool connected = await Task.Run(() => Instances.AsstProxy.AsstConnect(ref errMsg)); // 一般是点了“停止”按钮了 if (Stopping) { SetStopped(); return; } if (!connected) { AddLog(errMsg, UiLogColor.Error); AddLog(LocalizationHelper.GetString("ConnectFailed") + "\n" + LocalizationHelper.GetString("TryToStartEmulator")); await Task.Run(() => Instances.SettingsViewModel.TryToStartEmulator(true)); if (Stopping) { SetStopped(); return; } connected = await Task.Run(() => Instances.AsstProxy.AsstConnect(ref errMsg)); if (!connected) { AddLog(errMsg, UiLogColor.Error); Idle = true; SetStopped(); return; } } // 一般是点了“停止”按钮了 if (Stopping) { SetStopped(); return; } bool taskRet = true; // 直接遍历TaskItemViewModels里面的内容,是排序后的 int count = 0; foreach (var item in TaskItemViewModels) { if (item.IsChecked == false) { continue; } ++count; if (item.OriginalName == "Base") { taskRet &= appendInfrast(); } else if (item.OriginalName == "WakeUp") { taskRet &= appendStart(); } else if (item.OriginalName == "Combat") { taskRet &= appendFight(); } else if (item.OriginalName == "Recruiting") { taskRet &= appendRecruit(); } else if (item.OriginalName == "Mall") { taskRet &= appendMall(); } else if (item.OriginalName == "Mission") { taskRet &= Instances.AsstProxy.AsstAppendAward(); } else if (item.OriginalName == "AutoRoguelike") { taskRet &= AppendRoguelike(); } else if (item.OriginalName == "ReclamationAlgorithm") { taskRet &= AppendReclamation(); } else { --count; // TODO 报错 } if (!taskRet) { AddLog(item.OriginalName + "Error", UiLogColor.Error); taskRet = true; --count; } } if (count == 0) { AddLog(LocalizationHelper.GetString("UnselectedTask")); Idle = true; SetStopped(); return; } taskRet &= Instances.AsstProxy.AsstStart(); if (taskRet) { AddLog(LocalizationHelper.GetString("Running")); if (!Instances.SettingsViewModel.AdbReplaced && !Instances.SettingsViewModel.IsAdbTouchMode()) { AddLog(LocalizationHelper.GetString("AdbReplacementTips"), UiLogColor.Info); } } else { AddLog(LocalizationHelper.GetString("UnknownErrorOccurs")); await Stop(); SetStopped(); } } /// /// Stops. /// /// A representing the asynchronous operation. public async Task Stop() { Stopping = true; AddLog(LocalizationHelper.GetString("Stopping")); await Task.Run(Instances.AsstProxy.AsstStop); } public void SetStopped() { if (!Idle || Stopping) { AddLog(LocalizationHelper.GetString("Stopped")); } Stopping = false; Idle = true; } private bool appendStart() { var mode = Instances.SettingsViewModel.ClientType; var enable = mode.Length != 0; return Instances.AsstProxy.AsstAppendStartUp(mode, enable); } private bool appendFight() { int medicine = 0; if (UseMedicine) { if (!int.TryParse(MedicineNumber, out medicine)) { medicine = 0; } } int stone = 0; if (UseStone) { if (!int.TryParse(StoneNumber, out stone)) { stone = 0; } } int times = int.MaxValue; if (HasTimesLimited) { if (!int.TryParse(MaxTimes, out times)) { times = 0; } } int drops_quantity = 0; if (IsSpecifiedDrops) { if (!int.TryParse(DropsQuantity, out drops_quantity)) { drops_quantity = 0; } } string curStage = Stage; bool mainFightRet = Instances.AsstProxy.AsstAppendFight(curStage, medicine, stone, times, DropsItemId, drops_quantity); if (mainFightRet && (curStage == "Annihilation") && Instances.SettingsViewModel.UseAlternateStage) { foreach (var stage in new[] { Stage1, Stage2, Stage3 }) { if (IsStageOpen(stage) && (stage != curStage)) { mainFightRet = Instances.AsstProxy.AsstAppendFight(stage, medicine, 0, int.MaxValue, string.Empty, 0); } } } if (mainFightRet && UseRemainingSanityStage && (RemainingSanityStage != string.Empty)) { return Instances.AsstProxy.AsstAppendFight(RemainingSanityStage, 0, 0, int.MaxValue, string.Empty, 0, false); } return mainFightRet; } public bool EnableSetFightParams { get; set; } = true; /// /// Sets parameters. /// public void SetFightParams() { if (EnableSetFightParams) { int medicine = 0; if (UseMedicine) { if (!int.TryParse(MedicineNumber, out medicine)) { medicine = 0; } } int stone = 0; if (UseStone) { if (!int.TryParse(StoneNumber, out stone)) { stone = 0; } } int times = int.MaxValue; if (HasTimesLimited) { if (!int.TryParse(MaxTimes, out times)) { times = 0; } } int drops_quantity = 0; if (IsSpecifiedDrops) { if (!int.TryParse(DropsQuantity, out drops_quantity)) { drops_quantity = 0; } } Instances.AsstProxy.AsstSetFightTaskParams(Stage, medicine, stone, times, DropsItemId, drops_quantity); } } public void SetFightRemainingSanityParams() { Instances.AsstProxy.AsstSetFightTaskParams(RemainingSanityStage, 0, 0, int.MaxValue, string.Empty, 0, false); } public void SetInfrastParams() { var order = Instances.SettingsViewModel.GetInfrastOrderList(); Instances.AsstProxy.AsstSetInfrastTaskParams(order.ToArray(), Instances.SettingsViewModel.UsesOfDrones, Instances.SettingsViewModel.DormThreshold / 100.0, Instances.SettingsViewModel.DormFilterNotStationedEnabled, Instances.SettingsViewModel.DormTrustEnabled, Instances.SettingsViewModel.OriginiumShardAutoReplenishment, Instances.SettingsViewModel.CustomInfrastEnabled, Instances.SettingsViewModel.CustomInfrastFile, CustomInfrastPlanIndex); } private bool appendInfrast() { if (Instances.SettingsViewModel.CustomInfrastEnabled && !File.Exists(Instances.SettingsViewModel.CustomInfrastFile)) { AddLog(LocalizationHelper.GetString("CustomizeInfrastSelectionEmpty"), UiLogColor.Error); return false; } var order = Instances.SettingsViewModel.GetInfrastOrderList(); return Instances.AsstProxy.AsstAppendInfrast(order.ToArray(), Instances.SettingsViewModel.UsesOfDrones, Instances.SettingsViewModel.DormThreshold / 100.0, Instances.SettingsViewModel.DormFilterNotStationedEnabled, Instances.SettingsViewModel.DormTrustEnabled, Instances.SettingsViewModel.OriginiumShardAutoReplenishment, Instances.SettingsViewModel.CustomInfrastEnabled, Instances.SettingsViewModel.CustomInfrastFile, CustomInfrastPlanIndex); } private bool appendMall() { var buy_first = Instances.SettingsViewModel.CreditFirstList.Split(new char[] { ';', ';' }); var black_list = Instances.SettingsViewModel.CreditBlackList.Split(new char[] { ';', ';' }); for (var i = 0; i < buy_first.Length; ++i) { buy_first[i] = buy_first[i].Trim(); } for (var i = 0; i < black_list.Length; ++i) { black_list[i] = black_list[i].Trim(); } return Instances.AsstProxy.AsstAppendMall( this.Stage != string.Empty && Instances.SettingsViewModel.CreditFightTaskEnabled, Instances.SettingsViewModel.CreditShopping, buy_first, black_list, Instances.SettingsViewModel.CreditForceShoppingIfCreditFull); } private bool appendRecruit() { // for debug if (!int.TryParse(Instances.SettingsViewModel.RecruitMaxTimes, out var max_times)) { max_times = 0; } var reqList = new List(); var cfmList = new List(); if (Instances.SettingsViewModel.ChooseLevel3) { cfmList.Add(3); } if (Instances.SettingsViewModel.ChooseLevel4) { reqList.Add(4); cfmList.Add(4); } if (Instances.SettingsViewModel.ChooseLevel5) { reqList.Add(5); cfmList.Add(5); } return Instances.AsstProxy.AsstAppendRecruit( max_times, reqList.ToArray(), cfmList.ToArray(), Instances.SettingsViewModel.RefreshLevel3, Instances.SettingsViewModel.UseExpedited, Instances.SettingsViewModel.NotChooseLevel1, Instances.SettingsViewModel.IsLevel3UseShortTime); } private bool AppendRoguelike() { int.TryParse(Instances.SettingsViewModel.RoguelikeMode, out var mode); return Instances.AsstProxy.AsstAppendRoguelike( mode, Instances.SettingsViewModel.RoguelikeStartsCount, Instances.SettingsViewModel.RoguelikeInvestmentEnabled, Instances.SettingsViewModel.RoguelikeInvestsCount, Instances.SettingsViewModel.RoguelikeStopWhenInvestmentFull, Instances.SettingsViewModel.RoguelikeSquad, Instances.SettingsViewModel.RoguelikeRoles, Instances.SettingsViewModel.RoguelikeCoreChar, Instances.SettingsViewModel.RoguelikeUseSupportUnit, Instances.SettingsViewModel.RoguelikeEnableNonfriendSupport, Instances.SettingsViewModel.RoguelikeTheme, Instances.SettingsViewModel.RoguelikeRefreshTraderWithDice); } private bool AppendReclamation() { return Instances.AsstProxy.AsstAppendReclamation(); } [DllImport("User32.dll", EntryPoint = "FindWindow")] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowThreadProcessId(IntPtr hwnd, out int id); /// /// 一个根据连接配置判断使用关闭模拟器的方式的方法 /// /// 是否关闭成功 public bool KillEmulatorModeSwitcher() { string emulatorMode = Instances.SettingsViewModel.ConnectConfig; return emulatorMode switch { "Nox" => KillEmulatorNox(), "LDPlayer" => KillEmulatorLDPlayer(), "XYAZ" => KillEmulatorXYAZ(), "BlueStacks" => KillEmulatorBlueStacks(), "MuMuEmulator12" => KillEmulatorMuMuEmulator12(), _ => KillEumlatorbyWindow(), }; } /// /// 一个用于调用 MuMu12 模拟器控制台关闭 MuMu12 的方法 /// /// 是否关闭成功 public bool KillEmulatorMuMuEmulator12() { string address = Instances.SettingsViewModel.ConnectAddress; int emuIndex; if (address == "127.0.0.1:16384") { emuIndex = 0; } else { string portStr = address.Split(':')[1]; int port = int.Parse(portStr); emuIndex = (port - 16384) / 32; } Process[] processes = Process.GetProcessesByName("MuMuPlayer"); if (processes.Length > 0) { string emuLocation = processes[0].MainModule.FileName; emuLocation = Path.GetDirectoryName(emuLocation); string consolePath = Path.Combine(emuLocation, "MuMuManager.exe"); if (File.Exists(consolePath)) { ProcessStartInfo startInfo = new ProcessStartInfo(consolePath) { Arguments = $"api -v {emuIndex} shutdown_player", CreateNoWindow = true, UseShellExecute = false, }; var process = Process.Start(startInfo); process.WaitForExit(5000); return KillEmulator(); } else { _logger.Error($"Error: `{consolePath}` not found, try to kill eumlator by window."); return KillEumlatorbyWindow(); } } return false; } /// /// 一个用于调用雷电模拟器控制台关闭雷电模拟器的方法 /// /// 是否关闭成功 public bool KillEmulatorLDPlayer() { string address = Instances.SettingsViewModel.ConnectAddress; int emuIndex; if (address.Contains(":")) { string portStr = address.Split(':')[1]; int port = int.Parse(portStr); emuIndex = (port - 5555) / 2; } else { string portStr = address.Split('-')[1]; int port = int.Parse(portStr); emuIndex = (port - 5554) / 2; } Process[] processes = Process.GetProcessesByName("dnplayer"); if (processes.Length > 0) { string emuLocation = processes[0].MainModule.FileName; emuLocation = Path.GetDirectoryName(emuLocation); string consolePath = Path.Combine(emuLocation, "ldconsole.exe"); if (File.Exists(consolePath)) { ProcessStartInfo startInfo = new ProcessStartInfo(consolePath) { Arguments = $"quit --index {emuIndex}", CreateNoWindow = true, UseShellExecute = false, }; var process = Process.Start(startInfo); process.WaitForExit(5000); return KillEmulator(); } else { _logger.Information($"Error: `{consolePath}` not found, try to kill eumlator by window."); return KillEumlatorbyWindow(); } } return false; } /// /// 一个用于调用夜神模拟器控制台关闭夜神模拟器的方法 /// /// 是否关闭成功 public bool KillEmulatorNox() { string address = Instances.SettingsViewModel.ConnectAddress; int emuIndex; if (address == "127.0.0.1:62001") { emuIndex = 0; } else { string portStr = address.Split(':')[1]; int port = int.Parse(portStr); emuIndex = port - 62024; } Process[] processes = Process.GetProcessesByName("Nox"); if (processes.Length > 0) { string emuLocation = processes[0].MainModule.FileName; emuLocation = Path.GetDirectoryName(emuLocation); string consolePath = Path.Combine(emuLocation, "NoxConsole.exe"); if (File.Exists(consolePath)) { ProcessStartInfo startInfo = new ProcessStartInfo(consolePath) { Arguments = $"quit -index:{emuIndex}", CreateNoWindow = true, UseShellExecute = false, }; var process = Process.Start(startInfo); process.WaitForExit(5000); return KillEmulator(); } else { _logger.Information($"Error: `{consolePath}` not found, try to kill eumlator by window."); return KillEumlatorbyWindow(); } } return false; } /// /// 一个用于调用逍遥模拟器控制台关闭逍遥模拟器的方法 /// /// 是否关闭成功 public bool KillEmulatorXYAZ() { string address = Instances.SettingsViewModel.ConnectAddress; int emuIndex; string portStr = address.Split(':')[1]; int port = int.Parse(portStr); emuIndex = (port - 21503) / 10; Process[] processes = Process.GetProcessesByName("MEmu"); if (processes.Length > 0) { string emuLocation = processes[0].MainModule.FileName; emuLocation = Path.GetDirectoryName(emuLocation); string consolePath = Path.Combine(emuLocation, "memuc.exe"); if (File.Exists(consolePath)) { ProcessStartInfo startInfo = new ProcessStartInfo(consolePath) { Arguments = $"stop -i {emuIndex}", CreateNoWindow = true, UseShellExecute = false, }; var process = Process.Start(startInfo); process.WaitForExit(5000); return KillEmulator(); } else { _logger.Information($"Error: `{consolePath}` not found, try to kill eumlator by window."); return KillEumlatorbyWindow(); } } return false; } /// /// 一个用于关闭蓝叠模拟器的方法 /// /// 是否关闭成功 public bool KillEmulatorBlueStacks() { Process[] processes = Process.GetProcessesByName("HD-Player"); if (processes.Length > 0) { string emuLocation = processes[0].MainModule.FileName; emuLocation = Path.GetDirectoryName(emuLocation); string consolePath = Path.Combine(emuLocation, "bsconsole.exe"); if (File.Exists(consolePath)) { _logger.Information($"Info: `{consolePath}` has been found. This may be the BlueStacks China emulator, try to kill the emulator by window."); return KillEumlatorbyWindow(); } else { _logger.Information($"Info: `{consolePath}` not found. This may be the BlueStacks International emulator, try to kill the emulator by the port."); if (KillEmulator()) { return true; } else { try { _logger.Information($"Info: Failed to kill emulator by the port, try to kill emulator process with PID."); processes[0].Kill(); return processes[0].WaitForExit(20000); } catch (Exception ex) { _logger.Information($"Error: Failed to kill emulator process with PID {processes[0].Id}. Exception: {ex.Message}"); } } } } return false; } /// /// Kills emulator by Window hwnd. /// /// Whether the operation is successful. public bool KillEumlatorbyWindow() { IntPtr hwnd; int pid = 0; var windowname = new[] { "明日方舟", "明日方舟 - MuMu模拟器", "BlueStacks App Player", "BlueStacks", }; foreach (string i in windowname) { hwnd = FindWindow(null, i); if (hwnd != IntPtr.Zero) { GetWindowThreadProcessId(hwnd, out pid); break; } } do { if (pid == 0) { break; } Process emulator; try { emulator = Process.GetProcessById(pid); emulator.CloseMainWindow(); if (!emulator.WaitForExit(5000)) { emulator.Kill(); emulator.WaitForExit(5000); } else { // 尽管已经成功 CloseMainWindow(),再次尝试 killEmulator() // Refer to https://github.com/MaaAssistantArknights/MaaAssistantArknights/pull/1878 KillEmulator(); // 已经成功 CloseMainWindow(),所以不管 killEmulator() 的结果如何,都返回 true return true; } } catch { break; } } while (false); return KillEmulator(); } /// /// Kills emulator. /// /// Whether the operation is successful. public bool KillEmulator() { int pid = 0; string port; string address = ConfigurationHelper.GetValue(ConfigurationKeys.ConnectAddress, string.Empty); if (address.StartsWith("127")) { port = address.Substring(10); } else { port = "5555"; } string portCmd = "netstat -ano|findstr \"" + port + "\""; Process checkCmd = new Process(); checkCmd.StartInfo.FileName = "cmd.exe"; checkCmd.StartInfo.UseShellExecute = false; checkCmd.StartInfo.RedirectStandardInput = true; checkCmd.StartInfo.RedirectStandardOutput = true; checkCmd.StartInfo.RedirectStandardError = true; checkCmd.StartInfo.CreateNoWindow = true; try { checkCmd.Start(); } catch { checkCmd.Close(); return false; } checkCmd.StandardInput.WriteLine(portCmd); checkCmd.StandardInput.WriteLine("exit"); Regex reg = new Regex("\\s+", RegexOptions.Compiled); string line; while (true) { line = checkCmd.StandardOutput.ReadLine(); try { line = line.Trim(); } catch { break; } if (line.StartsWith("TCP", StringComparison.OrdinalIgnoreCase)) { line = reg.Replace(line, ","); string[] arr = line.Split(','); if (!Convert.ToBoolean(string.Compare(arr[1], address, StringComparison.Ordinal)) || !Convert.ToBoolean(string.Compare(arr[1], "[::]:" + port, StringComparison.Ordinal)) || !Convert.ToBoolean(string.Compare(arr[1], "0.0.0.0:" + port, StringComparison.Ordinal))) { pid = int.Parse(arr[4]); break; } } } if (pid == 0) { return false; } try { Process emulator = Process.GetProcessById(pid); emulator.Kill(); } catch { return false; } finally { checkCmd.Close(); } return true; } /// /// The action type. /// public enum ActionType { /// /// Does nothing. /// DoNothing, /// /// Stops game. /// StopGame, /// /// Exits MAA. /// ExitSelf, /// /// Exits emulator. /// ExitEmulator, /// /// Exits MAA and emulator. /// ExitEmulatorAndSelf, /// /// Computer suspends. /// Suspend, /// /// Computer hibernates. /// Hibernate, /// /// Exits MAA and emulator and computer hibernates. /// ExitEmulatorAndSelfAndHibernate, /// /// Computer shutdown. /// Shutdown, /// /// Computer hibernates without Persist. /// HibernateWithoutPersist, /// /// Exits MAA and emulator and computer hibernates without Persist. /// ExitEmulatorAndSelfAndHibernateWithoutPersist, /// /// Computer shutdown without Persist. /// ShutdownWithoutPersist, } /// /// Checks after completion. /// public void CheckAfterCompleted() { Task.Run(() => Instances.SettingsViewModel.RunScript("EndsWithScript")); switch (ActionAfterCompleted) { case ActionType.DoNothing: break; case ActionType.StopGame: if (!Instances.AsstProxy.AsstStartCloseDown()) { AddLog(LocalizationHelper.GetString("CloseArknightsFailed"), UiLogColor.Error); } break; case ActionType.ExitSelf: // Shutdown 会调用 OnExit 但 Exit 不会 Application.Current.Shutdown(); // Environment.Exit(0); break; case ActionType.ExitEmulator: if (!KillEmulatorModeSwitcher()) { AddLog(LocalizationHelper.GetString("ExitEmulatorFailed"), UiLogColor.Error); } break; case ActionType.ExitEmulatorAndSelf: if (!KillEmulatorModeSwitcher()) { AddLog(LocalizationHelper.GetString("ExitEmulatorFailed"), UiLogColor.Error); } // Shutdown 会调用 OnExit 但 Exit 不会 Application.Current.Shutdown(); // Environment.Exit(0); break; case ActionType.Shutdown: case ActionType.ShutdownWithoutPersist: Process.Start("shutdown.exe", "-s -t 60"); // 关机询问 var shutdownResult = MessageBoxHelper.Show(LocalizationHelper.GetString("AboutToShutdown"), LocalizationHelper.GetString("ShutdownPrompt"), MessageBoxButton.OK, MessageBoxImage.Question, ok: LocalizationHelper.GetString("Cancel")); if (shutdownResult == MessageBoxResult.OK) { Process.Start("shutdown.exe", "-a"); } break; case ActionType.Suspend: Process.Start("powercfg", "-h off"); Process.Start("rundll32.exe", "powrprof.dll,SetSuspendState 0,1,0"); Process.Start("powercfg", "-h on"); break; case ActionType.Hibernate: case ActionType.HibernateWithoutPersist: // 休眠提示 AddLog(LocalizationHelper.GetString("HibernatePrompt"), UiLogColor.Error); // 休眠不能加时间参数,https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues/1133 Process.Start("shutdown.exe", "-h"); break; case ActionType.ExitEmulatorAndSelfAndHibernate: case ActionType.ExitEmulatorAndSelfAndHibernateWithoutPersist: if (!KillEmulatorModeSwitcher()) { AddLog(LocalizationHelper.GetString("ExitEmulatorFailed"), UiLogColor.Error); } // 休眠提示 AddLog(LocalizationHelper.GetString("HibernatePrompt"), UiLogColor.Error); // 休眠不能加时间参数,https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues/1133 Process.Start("shutdown.exe", "-h"); // Shutdown 会调用 OnExit 但 Exit 不会 Application.Current.Shutdown(); // Environment.Exit(0); break; default: break; } } /// /// Gets a value indicating whether it is initialized. /// public bool Inited { get; private set; } = false; /// /// Sets it initialized. /// public void SetInited() { Inited = true; NotifyOfPropertyChange("Inited"); } private bool _idle = false; /// /// Gets or sets a value indicating whether it is idle. /// public bool Idle { get => _idle; set { SetAndNotify(ref _idle, value); Instances.SettingsViewModel.Idle = value; if (value) { FightTaskRunning = false; InfrastTaskRunning = false; } } } private bool _stopping = false; /// /// Gets or sets a value indicating whether `stop` is awaiting. /// public bool Stopping { get => _stopping; set => SetAndNotify(ref _stopping, value); } private bool _fightTaskRunning = false; /// /// Gets or sets a value indicating whether the battle task is running. /// public bool FightTaskRunning { get => _fightTaskRunning; set => SetAndNotify(ref _fightTaskRunning, value); } private bool _infrastTaskRunning = false; public bool InfrastTaskRunning { get => _infrastTaskRunning; set => SetAndNotify(ref _infrastTaskRunning, value); } /* private bool _shutdown = false; public bool Shutdown { get => return _shutdown; set { SetAndNotify(ref _shutdown, value); if (value) { Hibernate = false; Suspend = false; } } } private bool _hibernate = false; // 休眠 public bool Hibernate { get => return _hibernate; set { SetAndNotify(ref _hibernate, value); if (value) { Shutdown = false; Suspend = false; } } } private bool _suspend = false; // 待机 public bool Suspend { get => return _suspend; set { SetAndNotify(ref _suspend, value); if (value) { Shutdown = false; Hibernate = false; } } } */ private ObservableCollection _stageList = new ObservableCollection(); /// /// Gets or sets the list of stages. /// public ObservableCollection StageList { get => _stageList; set => SetAndNotify(ref _stageList, value); } public ObservableCollection RemainingSanityStageList { get; set; } = new ObservableCollection(); /// /// Gets the stage. /// public string Stage { get { if (Instances.SettingsViewModel.UseAlternateStage) { if (IsStageOpen(Stage1) || (CustomStageCode && !StageList.Any(x => x.Value == Stage1))) { return Stage1; } if (IsStageOpen(Stage2) || (CustomStageCode && !StageList.Any(x => x.Value == Stage2))) { return Stage2; } if (IsStageOpen(Stage3) || (CustomStageCode && !StageList.Any(x => x.Value == Stage3))) { return Stage3; } } return Stage1; } } private string ToUpperAndCheckStage(string value) { if (string.IsNullOrEmpty(value)) { return value; } value = value.ToUpper(); if (StageList != null) { foreach (var item in StageList) { if (value == item.Value) { break; } else if (value == item.Display) { value = item.Value; break; } } } return value; } 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) { value = ToUpperAndCheckStage(value); } SetAndNotify(ref _stage1, value); SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.Stage1, value); 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) { value = ToUpperAndCheckStage(value); } SetAndNotify(ref _stage2, value); SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.Stage2, value); UpdateDatePrompt(); } } private string _stage3 = ConfigurationHelper.GetValue(ConfigurationKeys.Stage3, string.Empty) ?? string.Empty; /// /// Gets or sets the stage2. /// public string Stage3 { get => _stage3; set { if (_stage3 == value) { return; } if (CustomStageCode) { value = ToUpperAndCheckStage(value); } SetAndNotify(ref _stage3, value); SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.Stage3, value); UpdateDatePrompt(); } } private bool _useAlternateStage = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.UseAlternateStage, bool.FalseString)); /// /// Gets or sets a value indicating whether to use alternate stage. /// public bool UseAlternateStage { get => _useAlternateStage; set => SetAndNotify(ref _useAlternateStage, value); } private bool _useRemainingSanityStage = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.UseRemainingSanityStage, bool.TrueString)); public bool UseRemainingSanityStage { get => _useRemainingSanityStage; set => SetAndNotify(ref _useRemainingSanityStage, value); } private bool _customStageCode = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.CustomStageCode, bool.FalseString)); /// /// Gets or sets a value indicating whether to use custom stage code. /// public bool CustomStageCode { get => _customStageCode; set { SetAndNotify(ref _customStageCode, value); } } private string _remainingSanityStage = ConfigurationHelper.GetValue(ConfigurationKeys.RemainingSanityStage, string.Empty) ?? string.Empty; public string RemainingSanityStage { get { return _remainingSanityStage; } set { if (_remainingSanityStage == value) { return; } if (CustomStageCode) { value = ToUpperAndCheckStage(value); } SetAndNotify(ref _remainingSanityStage, value); SetFightRemainingSanityParams(); ConfigurationHelper.SetValue(ConfigurationKeys.RemainingSanityStage, value); } } private bool _customInfrastEnabled = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.CustomInfrastEnabled, bool.FalseString)); public bool CustomInfrastEnabled { get => _customInfrastEnabled; set { SetAndNotify(ref _customInfrastEnabled, value); RefreshCustonInfrastPlan(); } } private int _customInfrastPlanIndex = Convert.ToInt32(ConfigurationHelper.GetValue(ConfigurationKeys.CustomInfrastPlanIndex, "0")); public int CustomInfrastPlanIndex { get => _customInfrastPlanIndex; set { if (value != _customInfrastPlanIndex) { var plan = CustomInfrastPlanInfoList[value]; AddLog(plan.Name, UiLogColor.Message); foreach (var period in plan.PeriodList) { AddLog(string.Format("[ {0:D2}:{1:D2} - {2:D2}:{3:D2} ]", period.BeginHour, period.BeginMinute, period.EndHour, period.EndMinute)); } if (plan.Description != string.Empty) { AddLog(plan.Description); } } SetAndNotify(ref _customInfrastPlanIndex, value); SetInfrastParams(); ConfigurationHelper.SetValue(ConfigurationKeys.CustomInfrastPlanIndex, value.ToString()); } } public ObservableCollection> CustomInfrastPlanList { get; set; } = new ObservableCollection>(); public struct CustomInfrastPlanInfo { public int Index; public string Name; public string Description; public string DescriptionPost; // 有效时间段 public struct Period { public int BeginHour; public int BeginMinute; public int EndHour; public int EndMinute; } public List PeriodList; } public List CustomInfrastPlanInfoList { get; set; } = new List(); private bool _customInfrastPlanHasPeriod = false; private bool _customInfrastInfoOutput = false; public void RefreshCustonInfrastPlan() { CustomInfrastPlanInfoList.Clear(); CustomInfrastPlanList.Clear(); _customInfrastPlanHasPeriod = false; if (!CustomInfrastEnabled) { return; } if (!File.Exists(Instances.SettingsViewModel.CustomInfrastFile)) { return; } try { string jsonStr = File.ReadAllText(Instances.SettingsViewModel.CustomInfrastFile); var root = (JObject)JsonConvert.DeserializeObject(jsonStr); if (_customInfrastInfoOutput && root.ContainsKey("title")) { AddLog(LocalizationHelper.GetString("CustomInfrastTitle"), UiLogColor.Message); AddLog(root["title"].ToString(), UiLogColor.Info); if (root.ContainsKey("description")) { AddLog(root["description"].ToString()); } } var plan_list = (JArray)root["plans"]; for (int i = 0; i < plan_list.Count; ++i) { var plan = (JObject)plan_list[i]; string display = plan.ContainsKey("name") ? plan["name"].ToString() : ("Plan " + ((char)('A' + i)).ToString()); CustomInfrastPlanList.Add(new GenericCombinedData { Display = display, Value = i }); string desc = plan.ContainsKey("description") ? plan["description"].ToString() : string.Empty; string descPost = plan.ContainsKey("description_post") ? plan["description_post"].ToString() : string.Empty; if (_customInfrastInfoOutput) { AddLog(display, UiLogColor.Message); } var periodList = new List(); if (plan.ContainsKey("period")) { var periodArray = (JArray)plan["period"]; foreach (var periodJson in periodArray) { var period = default(CustomInfrastPlanInfo.Period); string beginTime = periodJson[0].ToString(); var beginSplited = beginTime.Split(':'); period.BeginHour = int.Parse(beginSplited[0]); period.BeginMinute = int.Parse(beginSplited[1]); string endTime = periodJson[1].ToString(); var endSplited = endTime.Split(':'); period.EndHour = int.Parse(endSplited[0]); period.EndMinute = int.Parse(endSplited[1]); periodList.Add(period); if (_customInfrastInfoOutput) { AddLog(string.Format("[ {0:D2}:{1:D2} - {2:D2}:{3:D2} ]", period.BeginHour, period.BeginMinute, period.EndHour, period.EndMinute)); } } if (periodList.Count != 0) { _customInfrastPlanHasPeriod = true; } } if (_customInfrastInfoOutput && desc != string.Empty) { AddLog(desc); } if (_customInfrastInfoOutput && descPost != string.Empty) { AddLog(descPost); } CustomInfrastPlanInfoList.Add(new CustomInfrastPlanInfo { Index = i, Name = display, Description = desc, DescriptionPost = descPost, PeriodList = periodList, }); } _customInfrastInfoOutput = true; } catch (Exception) { _customInfrastInfoOutput = true; AddLog(LocalizationHelper.GetString("CustomInfrastFileParseFailed"), UiLogColor.Error); return; } refreshCustomInfrastPlanIndexByPeriod(); } private void refreshCustomInfrastPlanIndexByPeriod() { if (!CustomInfrastEnabled || !_customInfrastPlanHasPeriod || InfrastTaskRunning) { return; } static bool timeLess(int lHour, int lMin, int rHour, int rMin) => (lHour != rHour) ? (lHour < rHour) : (lMin <= rMin); var now = DateTime.Now; foreach (var plan in CustomInfrastPlanInfoList) { foreach (var period in plan.PeriodList) { if (timeLess(period.BeginHour, period.BeginMinute, now.Hour, now.Minute) && timeLess(now.Hour, now.Minute, period.EndHour, period.EndMinute)) { CustomInfrastPlanIndex = plan.Index; return; } } } } public void IncreaseCustomInfrastPlanIndex() { if (!CustomInfrastEnabled || _customInfrastPlanHasPeriod) { return; } AddLog(LocalizationHelper.GetString("CustomInfrastPlanIndexAutoSwitch"), UiLogColor.Message); var prePlanPostDesc = CustomInfrastPlanInfoList[CustomInfrastPlanIndex].DescriptionPost; if (prePlanPostDesc != string.Empty) { AddLog(prePlanPostDesc); } if (CustomInfrastPlanIndex >= CustomInfrastPlanList.Count - 1) { CustomInfrastPlanIndex = 0; } else { ++CustomInfrastPlanIndex; } } private bool _useMedicine = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.UseMedicine, bool.FalseString)); /// /// Gets or sets a value indicating whether to use medicine. /// public bool UseMedicine { get => _useMedicine; set { SetAndNotify(ref _useMedicine, value); if (!value) { UseStone = false; } SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.UseMedicine, value.ToString()); } } private string _medicineNumber = ConfigurationHelper.GetValue(ConfigurationKeys.UseMedicineQuantity, "999"); /// /// Gets or sets the amount of medicine used. /// public string MedicineNumber { get => _medicineNumber; set { if (_medicineNumber == value) { return; } SetAndNotify(ref _medicineNumber, value); // If the amount of medicine is 0, the stone is not used. UseStone = UseStone; SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.UseMedicineQuantity, MedicineNumber); } } private bool _useStone; /// /// Gets or sets a value indicating whether to use originiums. /// public bool UseStone { get => _useStone; set { // If the amount of medicine is 0, the stone is not used. if (!int.TryParse(MedicineNumber, out int result) || result == 0) { value = false; } SetAndNotify(ref _useStone, value); if (value) { UseMedicine = true; } SetFightParams(); } } private string _stoneNumber = ConfigurationHelper.GetValue(ConfigurationKeys.UseStoneQuantity, "0"); /// /// Gets or sets the amount of originiums used. /// public string StoneNumber { get => _stoneNumber; set { if (_stoneNumber == value) { return; } SetAndNotify(ref _stoneNumber, value); SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.UseStoneQuantity, StoneNumber); } } private bool _hasTimesLimited; /// /// Gets or sets a value indicating whether the number of times is limited. /// public bool HasTimesLimited { get => _hasTimesLimited; set { SetAndNotify(ref _hasTimesLimited, value); SetFightParams(); } } private string _maxTimes = ConfigurationHelper.GetValue(ConfigurationKeys.TimesLimitedQuantity, "5"); /// /// Gets or sets the max number of times. /// public string MaxTimes { get => _maxTimes; set { if (MaxTimes == value) { return; } SetAndNotify(ref _maxTimes, value); SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.TimesLimitedQuantity, MaxTimes); } } #region Drops private bool _isSpecifiedDrops = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.DropsEnable, bool.FalseString)); /// /// Gets or sets a value indicating whether the drops are specified. /// public bool IsSpecifiedDrops { get => _isSpecifiedDrops; set { SetAndNotify(ref _isSpecifiedDrops, value); SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.DropsEnable, value.ToString()); } } /// /// Gets or sets the list of all drops. /// public List AllDrops { get; set; } = new List(); /// /// 关卡不可掉落的材料 /// private static readonly HashSet excludedValues = new HashSet() { "3213", "3223", "3233", "3243", // 双芯片 "3253", "3263", "3273", "3283", "7001", "7002", "7003", "7004", // 许可/凭证 "4004", "4005", "3105", "3131", "3132", "3233", // 龙骨/加固建材 "6001", // 演习券 "3141", "4002", // 源石 "32001", // 芯片助剂 "30115", // 聚合剂 "30125", // 双极纳米片 "30135", // D32钢 "30145", // 晶体电子单元 "30155", // 烧结核凝晶 }; private void InitDrops() { foreach (var item in ItemListHelper.ArkItems) { var val = item.Key; // 不是数字的东西都是正常关卡不会掉的(大概吧) if (!int.TryParse(val, out _)) { continue; } var dis = item.Value.Name; if (excludedValues.Contains(val)) { continue; } AllDrops.Add(new CombinedData { Display = dis, Value = val }); } AllDrops.Sort((a, b) => { return string.Compare(a.Value, b.Value, StringComparison.Ordinal); }); DropsList = new ObservableCollection(AllDrops); } /// /// Gets or sets the list of drops. /// public ObservableCollection DropsList { get; set; } private string _dropsItemId = ConfigurationHelper.GetValue(ConfigurationKeys.DropsItemId, string.Empty); /// /// Gets or sets the item ID of drops. /// public string DropsItemId { get => _dropsItemId; set { SetAndNotify(ref _dropsItemId, value); SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.DropsItemId, DropsItemId); } } private string _dropsItemName = ConfigurationHelper.GetValue(ConfigurationKeys.DropsItemName, LocalizationHelper.GetString("NotSelected")); /// /// Gets or sets the item Name of drops. /// public string DropsItemName { get => _dropsItemName; set { SetAndNotify(ref _dropsItemName, value); SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.DropsItemName, DropsItemName); } } public void DropsListDropDownClosed() { foreach (var item in DropsList) { if (DropsItemName == item.Display) { DropsItemId = item.Value; if (DropsItemName != item.Display || DropsItemId != item.Value) { DropsItemName = LocalizationHelper.GetString("NotSelected"); } return; } } DropsItemName = LocalizationHelper.GetString("NotSelected"); } private string _dropsQuantity = ConfigurationHelper.GetValue(ConfigurationKeys.DropsQuantity, "5"); /// /// Gets or sets the quantity of drops. /// public string DropsQuantity { get => _dropsQuantity; set { SetAndNotify(ref _dropsQuantity, value); SetFightParams(); ConfigurationHelper.SetValue(ConfigurationKeys.DropsQuantity, DropsQuantity); } } /// /// DropsList ComboBox loaded /// /// Event sender /// Event args public void DropsList_Loaded(object sender, EventArgs e) { (sender as ComboBox)?.MakeComboBoxSearchable(); } #endregion Drops } }