From 3d42e5e55fd9fc0c09fcf8c9f8149ebfb5120882 Mon Sep 17 00:00:00 2001 From: uye <99072975+ABA2396@users.noreply.github.com> Date: Mon, 5 Jan 2026 20:30:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=88=90=E5=B0=B1=20dlc=20(#15288)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 成就描述与翻译 * feat: 开着日志悬浮窗完成一次任务 * feat: 在公开招募中刷新标签 50 次 * feat: 累计开启线索交流 20/50 次 * feat: 累计向好友发送 20/50 次线索 * feat: 单次运行中,勾选超过 7 个任务 * feat: 在更新发布后 1 天/1 小时 内完成更新 * feat: 完成{key=Combat}时刚好消耗完所有理智 * feat: 设置并使用一个超过 4 个修饰键的热键组合 * feat: 仓库识别功能识别到仓库内某种材料数量超过 5000 个(不包括作战记录!) * perf: 优化界面布局 * chore: 调整触发条件 * fix: 部分成就弹窗无法显示 * feat: 单轮公招任务连续招募 10/20 次 * feat: 支持组内排序 * perf: 优化成就排序 * feat: 干员识别中包含干员“帕拉斯” * feat: 使用远程协议调用错误的任务 id * feat: 尝试切换界面语言 * feat: 设置开机启动 MAA * feat: 连续 7 天每天集齐并开启线索交流 * feat: 325 * chore: 调整 325 触发条件 --- src/MaaWpfGui/Constants/AchievementIds.cs | 35 +++ .../Helper/AchievementTrackerHelper.cs | 262 +++++++++++++----- src/MaaWpfGui/Helper/Instances.cs | 15 +- src/MaaWpfGui/Main/AsstProxy.cs | 41 ++- src/MaaWpfGui/Models/Achievement.cs | 3 + .../Properties/BuildDateTimeAttribute.cs | 3 +- src/MaaWpfGui/Res/Localizations/en-us.xaml | 81 ++++++ src/MaaWpfGui/Res/Localizations/ja-jp.xaml | 81 ++++++ src/MaaWpfGui/Res/Localizations/ko-kr.xaml | 81 ++++++ src/MaaWpfGui/Res/Localizations/zh-cn.xaml | 81 ++++++ src/MaaWpfGui/Res/Localizations/zh-tw.xaml | 81 ++++++ .../RemoteControl/RemoteControlService.cs | 77 ++--- .../ViewModels/UI/TaskQueueViewModel.cs | 7 + .../ViewModels/UI/ToolboxViewModel.cs | 32 ++- .../Settings/GuiSettingsUserControlModel.cs | 3 +- .../Settings/StartSettingsUserControlModel.cs | 1 + .../Views/UI/AchievementListWindow.xaml | 40 ++- src/MaaWpfGui/Views/UI/ToolboxView.xaml | 2 +- .../UserControl/HotKeyEditorUserControl.xaml | 4 +- .../HotKeyEditorUserControl.xaml.cs | 20 ++ 20 files changed, 794 insertions(+), 156 deletions(-) diff --git a/src/MaaWpfGui/Constants/AchievementIds.cs b/src/MaaWpfGui/Constants/AchievementIds.cs index f0369c38e8..67e7d54b02 100644 --- a/src/MaaWpfGui/Constants/AchievementIds.cs +++ b/src/MaaWpfGui/Constants/AchievementIds.cs @@ -46,6 +46,19 @@ public static class AchievementIds public const string SanityExpire = "SanityExpire"; public const string OverLimitAgent = "OverLimitAgent"; + public const string RecruitGambler = "RecruitGambler"; + + public const string ClueUseGroup = "ClueUse"; + public const string ClueCollector = "ClueCollector"; + public const string CluePhilosopher = "CluePhilosopher"; + + public const string ClueSendGroup = "ClueSend"; + public const string ClueSharer = "ClueSharer"; + public const string CluePhilanthropist = "CluePhilanthropist"; + + public const string ClueObsession = "ClueObsession"; + public const string ClueObsessionCustomDataKey = "LastOpeningData"; + #endregion #region 功能探索类 @@ -69,6 +82,19 @@ public static class AchievementIds public const string PeekScreen = "PeekScreen"; public const string CustomizationMaster = "CustomizationMaster"; + public const string LogSupervisor = "LogSupervisor"; + public const string TaskChainKing = "TaskChainKing"; + public const string HotkeyMagician = "HotkeyMagician"; + public const string WarehouseMiser = "WarehouseMiser"; + + public const string HrManager = "HrManager"; + public const string HrSpecialist = "HrSpecialist"; + public const string HrSeniorSpecialist = "HrSeniorSpecialist"; + + public const string NotFound404 = "NotFound404"; + public const string Linguist = "Linguist"; + public const string StartupBoot = "StartupBoot"; + #endregion #region 代理关卡类 @@ -108,6 +134,8 @@ public static class AchievementIds public const string SnapshotChallenge5 = "SnapshotChallenge5"; // 10 public const string SnapshotChallenge6 = "SnapshotChallenge6"; // 5 + public const string Time325 = "Time325"; // 325 + #endregion #region BUG 相关 @@ -134,6 +162,10 @@ public static class AchievementIds public const string UseDaily2 = "UseDaily2"; // 30 天 public const string UseDaily3 = "UseDaily3"; // 365 天 + public const string UpdateGroup = "Update"; + public const string UpdateObsession = "UpdateObsession"; + public const string UpdateEarlyBird = "UpdateEarlyBird"; + #endregion #region 彩蛋类 @@ -147,5 +179,8 @@ public static class AchievementIds public const string MidnightLaunch = "MidnightLaunch"; public const string LunarNewYear = "LunarNewYear"; + public const string SanityPlanner = "SanityPlanner"; + public const string WarehouseKeeper = "WarehouseKeeper"; + #endregion } diff --git a/src/MaaWpfGui/Helper/AchievementTrackerHelper.cs b/src/MaaWpfGui/Helper/AchievementTrackerHelper.cs index 66fdb9105d..bf92be7f2a 100644 --- a/src/MaaWpfGui/Helper/AchievementTrackerHelper.cs +++ b/src/MaaWpfGui/Helper/AchievementTrackerHelper.cs @@ -42,15 +42,39 @@ public class AchievementTrackerHelper : PropertyChangedBase Sort(); if (Instances.MainWindowManager is not null) { - Instances.MainWindowManager.WindowRestored += (_, _) => - { - TryShowPendingGrowls(); + AttachWindowRestoredHandler(); + } + else + { + // 这里可以简化,因为事件触发时 MainWindowManager 肯定不为 null + Instances.MainWindowManagerInstantiated += (_, _) => { + var win = Instances.MainWindowManager?.GetWindowIfVisible(); + if (win == null) + { + AttachWindowRestoredHandler(); + } + else + { + TryShowPendingGrowls(); + } }; } SearchCmd = new RelayCommand(Search); } + private void AttachWindowRestoredHandler() + { + // 防止重复订阅 + Instances.MainWindowManager.WindowRestored -= OnWindowRestored; + Instances.MainWindowManager.WindowRestored += OnWindowRestored; + } + + private void OnWindowRestored(object? sender, EventArgs e) + { + TryShowPendingGrowls(); + } + public static AchievementTrackerHelper Instance { get; } = new(); private Dictionary _achievements = []; @@ -58,8 +82,7 @@ public class AchievementTrackerHelper : PropertyChangedBase public Dictionary Achievements { get => _achievements; - set - { + set { SetAndNotify(ref _achievements, value); NotifyOfPropertyChange(nameof(UnlockedCount)); NotifyOfPropertyChange(nameof(VisibleAchievements)); @@ -142,6 +165,7 @@ public class AchievementTrackerHelper : PropertyChangedBase .ThenByDescending(kv => kv.Value.IsNewUnlock) // 新解锁的排前面 .ThenBy(kv => kv.Value.Category) // 按类别分组 .ThenBy(kv => kv.Value.Group) + .ThenBy(kv => kv.Value.GroupIndex) // 组内排序 .ThenBy(kv => kv.Value.Id) // 最后按 Id .ToDictionary(kv => kv.Key, kv => kv.Value); } @@ -186,8 +210,7 @@ public class AchievementTrackerHelper : PropertyChangedBase achievement.IsNewUnlock = true; Save(); - var growlInfo = new GrowlInfo - { + var growlInfo = new GrowlInfo { IsCustom = true, Message = $"{LocalizationHelper.GetString("AchievementCelebrate")}: {achievement.Title}\n{achievement.Description}", StaysOpen = forceStayOpen || (staysOpen && !SettingsViewModel.AchievementSettings.AchievementPopupAutoClose), @@ -195,7 +218,7 @@ public class AchievementTrackerHelper : PropertyChangedBase IconKey = "HangoverGeometry", IconBrushKey = achievement.MedalBrushKey, }; - ShowInfo(growlInfo); + ShowInfo(growlInfo, forceStayOpen: forceStayOpen); } public async Task UnlockAll() @@ -250,17 +273,16 @@ public class AchievementTrackerHelper : PropertyChangedBase private static readonly List _pending = []; - public static void ShowInfo(GrowlInfo info) + public static void ShowInfo(GrowlInfo info, bool forceStayOpen = false) { // 检查是否禁用了成就通知 - if (SettingsViewModel.AchievementSettings.AchievementPopupDisabled) + if (SettingsViewModel.AchievementSettings.AchievementPopupDisabled && !forceStayOpen) { return; } - Execute.OnUIThread(() => - { - var win = Instances.MainWindowManager.GetWindowIfVisible(); + Execute.OnUIThread(() => { + var win = Instances.MainWindowManager?.GetWindowIfVisible(); if (win == null) { _pending.Add(info); @@ -273,12 +295,14 @@ public class AchievementTrackerHelper : PropertyChangedBase public static void TryShowPendingGrowls() { - foreach (var info in _pending) - { - Growl.Info(info); - } + Execute.OnUIThread(() => { + foreach (var info in _pending) + { + Growl.Info(info); + } - _pending.Clear(); + _pending.Clear(); + }); } private void CheckProgressUnlock(Achievement achievement) @@ -305,14 +329,37 @@ public class AchievementTrackerHelper : PropertyChangedBase Save(); } - public void AddProgressToGroup(string groupPrefix, int amount = 1) + public void AddProgressToGroup(string group, int amount = 1) { - foreach (var achievement in _achievements.Values.Where(achievement => achievement.Group == groupPrefix)) + foreach (var achievement in _achievements.Values.Where(achievement => achievement.Group == group)) { AddProgress(achievement.Id, amount); } } + public int GetProgress(string id) + { + if (!_achievements.TryGetValue(id, out var achievement)) + { + return 0; + } + + return achievement.Progress; + } + + public int GetProgressToGroup(string group) + { + var maxProgress = 0; + foreach (var achievement in _achievements.Values.Where(achievement => achievement.Group == group)) + { + if (achievement.Progress > maxProgress) + { + maxProgress = achievement.Progress; + } + } + return maxProgress; + } + public void SetProgress(string id, int progress) { if (!_achievements.TryGetValue(id, out var achievement)) @@ -325,9 +372,9 @@ public class AchievementTrackerHelper : PropertyChangedBase Save(); } - public void SetProgressToGroup(string groupPrefix, int progress) + public void SetProgressToGroup(string group, int progress) { - foreach (var achievement in _achievements.Values.Where(achievement => achievement.Group == groupPrefix)) + foreach (var achievement in _achievements.Values.Where(achievement => achievement.Group == group)) { SetProgress(achievement.Id, progress); } @@ -372,26 +419,26 @@ public class AchievementTrackerHelper : PropertyChangedBase #region 工厂函数 - private static Achievement BasicUsage(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false) - => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.BasicUsage, IsRare = isRare }; + private static Achievement BasicUsage(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false, int groupIndex = int.MaxValue) + => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.BasicUsage, IsRare = isRare, GroupIndex = groupIndex }; - private static Achievement FeatureExploration(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false) - => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.FeatureExploration, IsRare = isRare }; + private static Achievement FeatureExploration(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false, int groupIndex = int.MaxValue) + => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.FeatureExploration, IsRare = isRare, GroupIndex = groupIndex }; - private static Achievement AutoBattle(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false) - => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.AutoBattle, IsRare = isRare }; + private static Achievement AutoBattle(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false, int groupIndex = int.MaxValue) + => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.AutoBattle, IsRare = isRare, GroupIndex = groupIndex }; - private static Achievement Humor(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false) - => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.Humor, IsRare = isRare }; + private static Achievement Humor(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false, int groupIndex = int.MaxValue) + => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.Humor, IsRare = isRare, GroupIndex = groupIndex }; - private static Achievement BugRelated(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false) - => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.BugRelated, IsRare = isRare }; + private static Achievement BugRelated(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false, int groupIndex = int.MaxValue) + => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.BugRelated, IsRare = isRare, GroupIndex = groupIndex }; - private static Achievement Behavior(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false) - => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.Behavior, IsRare = isRare }; + private static Achievement Behavior(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false, int groupIndex = int.MaxValue) + => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.Behavior, IsRare = isRare, GroupIndex = groupIndex }; - private static Achievement EasterEgg(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false) - => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.EasterEgg, IsRare = isRare }; + private static Achievement EasterEgg(string id, string group = "", int? target = null, bool isHidden = false, bool isRare = false, int groupIndex = int.MaxValue) + => new() { Id = id, Group = group, Target = target ?? 0, IsHidden = isHidden, Category = AchievementCategory.EasterEgg, IsRare = isRare, GroupIndex = groupIndex }; #endregion @@ -399,22 +446,22 @@ public class AchievementTrackerHelper : PropertyChangedBase [ #region 基础使用类 - BasicUsage(id: AchievementIds.SanitySpender1, group: AchievementIds.SanitySpenderGroup, target: 10), // 理智作战次数 - BasicUsage(id: AchievementIds.SanitySpender2, group: AchievementIds.SanitySpenderGroup, target: 100), - BasicUsage(id: AchievementIds.SanitySpender3, group: AchievementIds.SanitySpenderGroup, target: 1000), + BasicUsage(id: AchievementIds.SanitySpender1, group: AchievementIds.SanitySpenderGroup, target: 10, groupIndex: 1), // 理智作战次数 + BasicUsage(id: AchievementIds.SanitySpender2, group: AchievementIds.SanitySpenderGroup, target: 100, groupIndex: 2), + BasicUsage(id: AchievementIds.SanitySpender3, group: AchievementIds.SanitySpenderGroup, target: 1000, groupIndex: 3), - BasicUsage(id: AchievementIds.SanitySaver1, group: AchievementIds.SanitySaverGroup, target: 1), // 使用理智药数 - BasicUsage(id: AchievementIds.SanitySaver2, group: AchievementIds.SanitySaverGroup, target: 10), - BasicUsage(id: AchievementIds.SanitySaver3, group: AchievementIds.SanitySaverGroup, target: 50), + BasicUsage(id: AchievementIds.SanitySaver1, group: AchievementIds.SanitySaverGroup, target: 1, groupIndex: 1), // 使用理智药数 + BasicUsage(id: AchievementIds.SanitySaver2, group: AchievementIds.SanitySaverGroup, target: 10, groupIndex: 2), + BasicUsage(id: AchievementIds.SanitySaver3, group: AchievementIds.SanitySaverGroup, target: 50, groupIndex: 3), - BasicUsage(id: AchievementIds.RoguelikeGamePass1, group: AchievementIds.RoguelikeGamePassGroup, target: 1), // 使用牛牛通关肉鸽 - BasicUsage(id: AchievementIds.RoguelikeGamePass2, group: AchievementIds.RoguelikeGamePassGroup, target: 5), - BasicUsage(id: AchievementIds.RoguelikeGamePass3, group: AchievementIds.RoguelikeGamePassGroup, target: 10), + BasicUsage(id: AchievementIds.RoguelikeGamePass1, group: AchievementIds.RoguelikeGamePassGroup, target: 1, groupIndex: 1), // 使用牛牛通关肉鸽 + BasicUsage(id: AchievementIds.RoguelikeGamePass2, group: AchievementIds.RoguelikeGamePassGroup, target: 5, groupIndex: 2), + BasicUsage(id: AchievementIds.RoguelikeGamePass3, group: AchievementIds.RoguelikeGamePassGroup, target: 10, groupIndex: 3), - BasicUsage(id: AchievementIds.RoguelikeN04, group: AchievementIds.RoguelikeNGroup), // 肉鸽 难度 通关 - BasicUsage(id: AchievementIds.RoguelikeN08, group: AchievementIds.RoguelikeNGroup), - BasicUsage(id: AchievementIds.RoguelikeN12, group: AchievementIds.RoguelikeNGroup), - BasicUsage(id: AchievementIds.RoguelikeN15, group: AchievementIds.RoguelikeNGroup, isRare: true), + BasicUsage(id: AchievementIds.RoguelikeN04, group: AchievementIds.RoguelikeNGroup, groupIndex: 1), // 肉鸽 难度 通关 + BasicUsage(id: AchievementIds.RoguelikeN08, group: AchievementIds.RoguelikeNGroup, groupIndex: 2), + BasicUsage(id: AchievementIds.RoguelikeN12, group: AchievementIds.RoguelikeNGroup, groupIndex: 3), + BasicUsage(id: AchievementIds.RoguelikeN15, group: AchievementIds.RoguelikeNGroup, isRare: true, groupIndex: 4), BasicUsage(id: AchievementIds.RoguelikeRetreat, group: AchievementIds.RoguelikeGroup, target: 100), // 牛牛放弃探索 100 次 BasicUsage(id: AchievementIds.RoguelikeGoldMax, group: AchievementIds.RoguelikeGroup, target: 999), // 肉鸽源石锭到达 999 @@ -422,33 +469,54 @@ public class AchievementTrackerHelper : PropertyChangedBase BasicUsage(id: AchievementIds.FirstLaunch), // 首次启动 BasicUsage(id: AchievementIds.SanityExpire, target: 8), // 单次消耗 8 瓶快过期的理智药 BasicUsage(id: AchievementIds.OverLimitAgent, target: 100, isHidden: true), // 单次代理 100 关 + + BasicUsage(id: AchievementIds.RecruitGambler, target: 50), // 公招赌徒 + + BasicUsage(id: AchievementIds.ClueCollector, group: AchievementIds.ClueUseGroup, target: 20, groupIndex: 1), // 线索搜集 + BasicUsage(id: AchievementIds.CluePhilosopher, group: AchievementIds.ClueUseGroup, target: 50, groupIndex: 2), // 线索哲学家 + BasicUsage(id: AchievementIds.ClueObsession, target: 7, isRare: true, groupIndex: 3), // 线索强迫症 + + BasicUsage(id: AchievementIds.ClueSharer, group: AchievementIds.ClueSendGroup, target: 20, groupIndex: 1), // 线索分享 + BasicUsage(id: AchievementIds.CluePhilanthropist, group: AchievementIds.ClueSendGroup, target: 50, groupIndex: 2), // 线索慈善家 #endregion #region 功能探索类 - FeatureExploration(id: AchievementIds.ScheduleMaster1, group: AchievementIds.ScheduleMasterGroup, target: 1), // 定时执行 - FeatureExploration(id: AchievementIds.ScheduleMaster2, group: AchievementIds.ScheduleMasterGroup, target: 100), + FeatureExploration(id: AchievementIds.ScheduleMaster1, group: AchievementIds.ScheduleMasterGroup, target: 1, groupIndex: 1), // 定时执行 + FeatureExploration(id: AchievementIds.ScheduleMaster2, group: AchievementIds.ScheduleMasterGroup, target: 100, groupIndex: 2), FeatureExploration(id: AchievementIds.MirrorChyanFirstUse, group: AchievementIds.MirrorChyanGroup, isHidden: true), // 第一次成功使用 MirrorChyan 下载 FeatureExploration(id: AchievementIds.MirrorChyanCdkError, group: AchievementIds.MirrorChyanGroup, isHidden: true), // MirrorChyan CDK 错误 - FeatureExploration(id: AchievementIds.Pioneer1, group: AchievementIds.PioneerGroup), // 将 MAA 更新至公测版 - FeatureExploration(id: AchievementIds.Pioneer2, group: AchievementIds.PioneerGroup, isHidden: true), // 将 MAA 更新至内测版(隐藏) - FeatureExploration(id: AchievementIds.Pioneer3, group: AchievementIds.PioneerGroup, isHidden: true), // 使用未发布版本的 MAA(隐藏) + FeatureExploration(id: AchievementIds.Pioneer1, group: AchievementIds.PioneerGroup, groupIndex: 1), // 将 MAA 更新至公测版 + FeatureExploration(id: AchievementIds.Pioneer2, group: AchievementIds.PioneerGroup, isHidden: true, groupIndex: 2), // 将 MAA 更新至内测版(隐藏) + FeatureExploration(id: AchievementIds.Pioneer3, group: AchievementIds.PioneerGroup, isHidden: true, groupIndex: 3), // 使用未发布版本的 MAA(隐藏) FeatureExploration(id: AchievementIds.MosquitoLeg, target: 5), // 使用「借助战打 OF-1」功能超过 5 次 FeatureExploration(id: AchievementIds.RealGacha, isHidden: true), // 真正的抽卡 FeatureExploration(id: AchievementIds.PeekScreen, isHidden: true), // 窥屏 FeatureExploration(id: AchievementIds.CustomizationMaster, isHidden: true), // 自定义背景 + + FeatureExploration(id: AchievementIds.LogSupervisor), // 超级监工 + FeatureExploration(id: AchievementIds.TaskChainKing, target: 7), // 任务链王 + FeatureExploration(id: AchievementIds.HotkeyMagician), // 热键魔术师 + FeatureExploration(id: AchievementIds.WarehouseMiser, target: 10000), // 仓库守财奴 + + FeatureExploration(id: AchievementIds.HrSpecialist, group: AchievementIds.HrManager, target: 10, groupIndex: 1), // 人事部专员 + FeatureExploration(id: AchievementIds.HrSeniorSpecialist, group: AchievementIds.HrManager, target: 20, groupIndex: 2), // 人事部高级专员 + + FeatureExploration(id: AchievementIds.NotFound404, isHidden: true), // 404! + FeatureExploration(id: AchievementIds.Linguist), // 语言学家 + FeatureExploration(id: AchievementIds.StartupBoot), // 开机启动 #endregion #region 自动战斗 - AutoBattle(id: AchievementIds.UseCopilot1, group: AchievementIds.UseCopilotGroup, target: 1), // 自动战斗 - AutoBattle(id: AchievementIds.UseCopilot2, group: AchievementIds.UseCopilotGroup, target: 10), - AutoBattle(id: AchievementIds.UseCopilot3, group: AchievementIds.UseCopilotGroup, target: 100), + AutoBattle(id: AchievementIds.UseCopilot1, group: AchievementIds.UseCopilotGroup, target: 1, groupIndex: 1), // 自动战斗 + AutoBattle(id: AchievementIds.UseCopilot2, group: AchievementIds.UseCopilotGroup, target: 10, groupIndex: 2), + AutoBattle(id: AchievementIds.UseCopilot3, group: AchievementIds.UseCopilotGroup, target: 100, groupIndex: 3), - AutoBattle(id: AchievementIds.CopilotLikeGiven1, group: AchievementIds.CopilotLikeGroup, target: 1), // 点赞 1 次 - AutoBattle(id: AchievementIds.CopilotLikeGiven2, group: AchievementIds.CopilotLikeGroup, target: 10), // 点赞 10 次 - AutoBattle(id: AchievementIds.CopilotLikeGiven3, group: AchievementIds.CopilotLikeGroup, target: 50), // 点赞 50 次 + AutoBattle(id: AchievementIds.CopilotLikeGiven1, group: AchievementIds.CopilotLikeGroup, target: 1, groupIndex: 1), // 点赞 1 次 + AutoBattle(id: AchievementIds.CopilotLikeGiven2, group: AchievementIds.CopilotLikeGroup, target: 10, groupIndex: 2), // 点赞 10 次 + AutoBattle(id: AchievementIds.CopilotLikeGiven3, group: AchievementIds.CopilotLikeGroup, target: 50, groupIndex: 3), // 点赞 50 次 AutoBattle(id: AchievementIds.CopilotError), // 代理作战出现失误 AutoBattle(id: AchievementIds.MapOutdated, isHidden: true), // 提示需要更新地图资源 @@ -456,20 +524,22 @@ public class AchievementTrackerHelper : PropertyChangedBase #endregion #region 搞笑/梗类成就 - Humor(id: AchievementIds.SnapshotChallenge1, group: AchievementIds.SnapshotChallengeGroup, isHidden: true), // 平均截图用时超过 800ms(高 ping 战士) - Humor(id: AchievementIds.SnapshotChallenge2, group: AchievementIds.SnapshotChallengeGroup, isHidden: true), // 平均截图用时在 400ms 到 800ms 之间(是不是有点太慢了) - Humor(id: AchievementIds.SnapshotChallenge3, group: AchievementIds.SnapshotChallengeGroup), // 平均截图用时小于 400ms(截图挑战 · Normal) - Humor(id: AchievementIds.SnapshotChallenge4, group: AchievementIds.SnapshotChallengeGroup), // 平均截图用时小于 100ms(截图挑战 · Fast) - Humor(id: AchievementIds.SnapshotChallenge5, group: AchievementIds.SnapshotChallengeGroup), // 平均截图用时小于 10ms(截图挑战 · Ultra) - Humor(id: AchievementIds.SnapshotChallenge6, group: AchievementIds.SnapshotChallengeGroup, isHidden: true, isRare: true), // 平均截图用时小于 5ms + Humor(id: AchievementIds.SnapshotChallenge1, group: AchievementIds.SnapshotChallengeGroup, isHidden: true, groupIndex: 6), // 平均截图用时超过 800ms(高 ping 战士) + Humor(id: AchievementIds.SnapshotChallenge2, group: AchievementIds.SnapshotChallengeGroup, isHidden: true, groupIndex: 5), // 平均截图用时在 400ms 到 800ms 之间(是不是有点太慢了) + Humor(id: AchievementIds.SnapshotChallenge3, group: AchievementIds.SnapshotChallengeGroup, groupIndex: 1), // 平均截图用时小于 400ms(截图挑战 · Normal) + Humor(id: AchievementIds.SnapshotChallenge4, group: AchievementIds.SnapshotChallengeGroup, groupIndex: 2), // 平均截图用时小于 100ms(截图挑战 · Fast) + Humor(id: AchievementIds.SnapshotChallenge5, group: AchievementIds.SnapshotChallengeGroup, groupIndex: 3), // 平均截图用时小于 10ms(截图挑战 · Ultra) + Humor(id: AchievementIds.SnapshotChallenge6, group: AchievementIds.SnapshotChallengeGroup, isHidden: true, isRare: true, groupIndex: 4), // 平均截图用时小于 5ms Humor(id: AchievementIds.QuickCloser, isHidden: true), // 快速关闭弹窗 Humor(id: AchievementIds.TacticalRetreat), // 停止任务 Humor(id: AchievementIds.Martian, isHidden: true), // 90 天没更新 Humor(id: AchievementIds.AnnouncementStubbornClick, isHidden: true), // 不看公告 - Humor(id: AchievementIds.RecruitNoSixStar, group: AchievementIds.RecruitGroup, target: 500), // 公招中累计 500 次没出现六星tag - Humor(id: AchievementIds.RecruitNoSixStarStreak, group: AchievementIds.RecruitGroup, target: 500, isHidden: true), // 公招中连续 500 次没出现六星tag + Humor(id: AchievementIds.RecruitNoSixStar, group: AchievementIds.RecruitGroup, target: 500, groupIndex: 1), // 公招中累计 500 次没出现六星tag + Humor(id: AchievementIds.RecruitNoSixStarStreak, group: AchievementIds.RecruitGroup, target: 500, isHidden: true, groupIndex: 2), // 公招中连续 500 次没出现六星tag + + Humor(id: AchievementIds.Time325, isHidden: true), // 325 #endregion #region BUG 相关 @@ -486,9 +556,12 @@ public class AchievementTrackerHelper : PropertyChangedBase Behavior(id: AchievementIds.TaskStartCancel, isHidden: true), // 在开始任务后马上又停止 Behavior(id: AchievementIds.AfkWatcher), // 窗口尺寸最小化后长时间不操作 - Behavior(id: AchievementIds.UseDaily1, group: AchievementIds.UseDailyGroup, target: 7), // 连续使用时间 - Behavior(id: AchievementIds.UseDaily2, group: AchievementIds.UseDailyGroup, target: 30), - Behavior(id: AchievementIds.UseDaily3, group: AchievementIds.UseDailyGroup, target: 365, isRare: true), + Behavior(id: AchievementIds.UseDaily1, group: AchievementIds.UseDailyGroup, target: 7, groupIndex: 1), // 连续使用时间 + Behavior(id: AchievementIds.UseDaily2, group: AchievementIds.UseDailyGroup, target: 30, groupIndex: 2), + Behavior(id: AchievementIds.UseDaily3, group: AchievementIds.UseDailyGroup, target: 365, isRare: true, groupIndex: 3), + + Behavior(id: AchievementIds.UpdateObsession, group: AchievementIds.UpdateGroup, groupIndex: 1), // 更新强迫症 + Behavior(id: AchievementIds.UpdateEarlyBird, group: AchievementIds.UpdateGroup, isHidden: true, groupIndex: 2), // 更新尝鲜者 #endregion #region 彩蛋类 @@ -500,6 +573,9 @@ public class AchievementTrackerHelper : PropertyChangedBase EasterEgg(id: AchievementIds.LunarNewYear, AchievementIds.LoginGroup, isHidden: true), // 春节 EasterEgg(id: AchievementIds.Lucky, isHidden: true, isRare: true), // 启动 MAA 时有极小概率触发 + + EasterEgg(id: AchievementIds.SanityPlanner, isRare: true), // 理智规划师 + EasterEgg(id: AchievementIds.WarehouseKeeper, isHidden: true), // 我是仓管! #endregion ]; @@ -513,6 +589,9 @@ public class AchievementTrackerHelper : PropertyChangedBase #region 带有 CustomData 的辅助函数 + /// + /// 一天开启 3 次任务 + /// public void MissionStartCountAdd() { const string Id = AchievementIds.MissionStartCount; @@ -532,6 +611,9 @@ public class AchievementTrackerHelper : PropertyChangedBase } } + /// + /// 连续使用 365 天 + /// public void UseDailyAdd() { // group 是不注册的,从第一个成就取 CustomData @@ -563,6 +645,34 @@ public class AchievementTrackerHelper : PropertyChangedBase SetAchievementCustomData(Id, Key, JToken.FromObject(today)); } + /// + /// 连续 7 天开启线索交流 + /// + public void ClueObsessionAdd() + { + const string Id = AchievementIds.ClueObsession; + const string Key = AchievementIds.ClueObsessionCustomDataKey; + var today = DateTime.UtcNow.ToYjDate().Date; + DateTime? lastDate = GetAchievementCustomData(Id, Key)?.ToObject(); + if (lastDate.HasValue) { + var delta = (today - lastDate.Value).TotalDays; + switch (delta) { + case 1: + AddProgress(Id); + break; + case > 1: + SetProgress(Id, 1); + break; + } + } + else + { + SetProgress(Id, 1); + } + + SetAchievementCustomData(Id, Key, JToken.FromObject(today)); + } + #endregion public static class Events @@ -581,6 +691,16 @@ public class AchievementTrackerHelper : PropertyChangedBase Instance.Unlock(AchievementIds.Martian); } + var totalHours = (DateTime.UtcNow - VersionUpdateSettingsUserControlModel.BuildDateTime).TotalHours; + if (totalHours <= 1) + { + Instance.Unlock(AchievementIds.UpdateEarlyBird); + } + if (totalHours <= 24) + { + Instance.Unlock(AchievementIds.UpdateObsession); + } + if (Instances.VersionUpdateViewModel.IsDebugVersion()) { Instance.Unlock(AchievementIds.Pioneer3); diff --git a/src/MaaWpfGui/Helper/Instances.cs b/src/MaaWpfGui/Helper/Instances.cs index 099d6a054c..163e91952a 100644 --- a/src/MaaWpfGui/Helper/Instances.cs +++ b/src/MaaWpfGui/Helper/Instances.cs @@ -13,6 +13,7 @@ #pragma warning disable SA1401 +using System; using GlobalHotKey; using MaaWpfGui.Main; using MaaWpfGui.Services; @@ -34,13 +35,15 @@ public static class Instances { public static class Data { - public static int MedicineUsedTimes { get; set; } + public static int MedicineUsedTimes { get; set; } = 0; - public static int ExpiringMedicineUsedTimes { get; set; } + public static int ExpiringMedicineUsedTimes { get; set; } = 0; - public static int StoneUsedTimes { get; set; } + public static int StoneUsedTimes { get; set; } = 0; - public static bool HasPrintedScreencapWarning { get; set; } + public static bool HasPrintedScreencapWarning { get; set; } = false; + + public static int RecruitConfirmTime { get; set; } = 0; public static void ClearCache() { @@ -48,6 +51,7 @@ public static class Instances MedicineUsedTimes = 0; ExpiringMedicineUsedTimes = 0; StoneUsedTimes = 0; + RecruitConfirmTime = 0; HasPrintedScreencapWarning = false; } } @@ -84,6 +88,8 @@ public static class Instances public static IMainWindowManager MainWindowManager { get; private set; } + public static event EventHandler MainWindowManagerInstantiated; + public static IHttpService HttpService { get; private set; } public static IMaaApiService MaaApiService { get; private set; } @@ -120,5 +126,6 @@ public static class Instances public static void InstantiateOnRootViewDisplayed(IContainer container) { MainWindowManager = container.Get(); + MainWindowManagerInstantiated?.Invoke(null, EventArgs.Empty); } } diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs index 52906011ff..4b14d2a9f9 100644 --- a/src/MaaWpfGui/Main/AsstProxy.cs +++ b/src/MaaWpfGui/Main/AsstProxy.cs @@ -1080,6 +1080,11 @@ public class AsstProxy { var sanityLog = "\n" + string.Format(LocalizationHelper.GetString("CurrentSanity"), FightTask.SanityReport.SanityCurrent, FightTask.SanityReport.SanityMax); Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("CompleteTask") + LocalizationHelper.GetString(taskChain) + sanityLog); + + if (FightTask.SanityReport.SanityCurrent == 0) + { + AchievementTrackerHelper.Instance.Unlock(AchievementIds.SanityPlanner); + } } else { @@ -1232,6 +1237,11 @@ public class AsstProxy // Instances.TaskQueueViewModel.CheckAndShutdown(); _ = Instances.TaskQueueViewModel.CheckAfterCompleted(); + + if (Instances.OverlayViewModel.IsCreated) + { + AchievementTrackerHelper.Instance.Unlock(AchievementIds.LogSupervisor); + } } else if (isCopilotTaskChain) { @@ -1502,6 +1512,12 @@ public class AsstProxy break; case "RecruitConfirm": + RecruitConfirmTime++; + if (RecruitConfirmTime > AchievementTrackerHelper.Instance.GetProgressToGroup(AchievementIds.HrManager)) + { + AchievementTrackerHelper.Instance.AddProgressToGroup(AchievementIds.HrManager); + } + Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("RecruitConfirm"), UiLogColor.Info); break; @@ -1628,12 +1644,29 @@ public class AsstProxy switch (subTask) { case "ProcessTask": - var taskchain = details["taskchain"]?.ToString(); - switch (taskchain) + var taskName = details["details"]?["task"]?.ToString(); + var taskChain = details["taskchain"]?.ToString(); + switch (taskChain) { + case "Infrast": + { + switch (taskName) + { + case "UnlockClues": + Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("ClueExchangeUnlocked")); + AchievementTrackerHelper.Instance.AddProgressToGroup(AchievementIds.ClueUseGroup); + AchievementTrackerHelper.Instance.ClueObsessionAdd(); + break; + case "SendClues": + AchievementTrackerHelper.Instance.AddProgressToGroup(AchievementIds.ClueSendGroup); + break; + } + + break; + } + case "Roguelike": { - var taskName = details!["details"]!["task"]!.ToString(); int execTimes = (int)details!["details"]!["exec_times"]!; if (taskName == "StartExplore") @@ -1646,7 +1679,6 @@ public class AsstProxy case "Mall": { - var taskName = details["details"]!["task"]!.ToString(); switch (taskName) { case "EndOfActionThenStop": @@ -1875,6 +1907,7 @@ public class AsstProxy { int refreshCount = (int)subTaskDetails!["count"]!; Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("Refreshed") + refreshCount + LocalizationHelper.GetString("UnitTime")); + AchievementTrackerHelper.Instance.AddProgress(AchievementIds.RecruitGambler); break; } diff --git a/src/MaaWpfGui/Models/Achievement.cs b/src/MaaWpfGui/Models/Achievement.cs index 9f368297b3..4469a30e7b 100644 --- a/src/MaaWpfGui/Models/Achievement.cs +++ b/src/MaaWpfGui/Models/Achievement.cs @@ -83,6 +83,9 @@ public class Achievement [JsonIgnore] public string Group { get; set; } = string.Empty; + [JsonIgnore] + public int GroupIndex { get; set; } = int.MaxValue; + [JsonIgnore] public string MedalBrushKey { diff --git a/src/MaaWpfGui/Properties/BuildDateTimeAttribute.cs b/src/MaaWpfGui/Properties/BuildDateTimeAttribute.cs index 7322eb2ad5..acabf35599 100644 --- a/src/MaaWpfGui/Properties/BuildDateTimeAttribute.cs +++ b/src/MaaWpfGui/Properties/BuildDateTimeAttribute.cs @@ -12,11 +12,12 @@ // using System; +using System.Globalization; namespace MaaWpfGui.Properties; [AttributeUsage(AttributeTargets.Assembly)] public class BuildDateTimeAttribute(string date) : Attribute { - public DateTime BuildDateTime { get; } = DateTime.ParseExact(date, "O", null); + public DateTime BuildDateTime { get; } = DateTime.ParseExact(date, "O", null, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); } diff --git a/src/MaaWpfGui/Res/Localizations/en-us.xaml b/src/MaaWpfGui/Res/Localizations/en-us.xaml index 7d855eb860..7cd22f8372 100644 --- a/src/MaaWpfGui/Res/Localizations/en-us.xaml +++ b/src/MaaWpfGui/Res/Localizations/en-us.xaml @@ -122,6 +122,7 @@ Originium Shard auto replenishment Credit collection from message board in Reception Room Conduct Clue Exchange + Clue Exchange Unlocked Send Clues Requires Infrastructure Shift After training is complete, try to continue mastering the current skill @@ -1595,6 +1596,86 @@ If you want to run multiple MAA at the same time, please copy the whole MAA and Immortal Arts Cup Contestant Immortal MAA truly immortal. Clear any I.S. at N15 difficulty or above using 「{key=AutoRoguelike}」 + + Supervisor + They say watching MAA's logs can effectively improve auto-battle success rate. + Complete a task with the log floating window open + + Recruitment Gambler + All in! Turn a bicycle into a motorcycle. + Refresh tags 50 times in public recruitment + + Clue Collector + Rhodes Island socialite. + Open clue exchange 20 times in total + + Clue Philosopher + What is the meaning of gathering? + Open clue exchange 50 times in total + + Clue Sharer + Good things should be shared with friends. + Send clues to friends 20 times in total + + Clue Philanthropist + Take it, no need to thank me. + Send clues to friends 50 times in total + + Task Chain King + One click to start, everything done. + Select more than 7 tasks in a single run + + Update Obsession + Update available? Update! + Complete update within 1 day after release + + Update Early Bird + Be the first to experience! + Complete update within 1 hour after release + + Sanity Planner + Precise, perfect, not a single point off. + Complete {key=Combat} with exactly all sanity consumed + + Hotkey Magician + Ctrl + Shift + Alt + Win + F13 + Set and use a hotkey combination with more than 4 modifier keys + + Warehouse Miser + Not a single one shall be used! + Warehouse recognition detects more than 10000 of a material + + HR Specialist + Why are they all Reserve Action Teams! + Recruit 10 times consecutively in a single recruitment task + + HR Senior Specialist + Where is the Senior Operator tag? Where is it?🎶 + Recruit 20 times consecutively in a single recruitment task + + I'm the Warehouse Keeper! + MAA found MAA! + Operator recognition includes operator "Pallas" + + 404! + This task seems to be missing + Use remote protocol to call a task with incorrect id + + Linguist + MAA, but internationalized. + Try switching interface language + + Startup Boot + Theoretically, your boot time just got a bit slower + Set MAA to start on boot + + Clue Obsession + Seven-color team, assemble! + Collect and open clue exchange every day for 7 consecutive days + + 325 + You know what this number means, right? + Still using MAA at 3/13/23:25 diff --git a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml index 3935665bd4..6e92c1916a 100644 --- a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml +++ b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml @@ -122,6 +122,7 @@ 源石の欠片を自動で補充 受付室の掲示板からのクレジット収集 手がかり交換を実施 + 手がかり交換を開始しました 手がかりを贈る インフラシフト実行が必要 訓練完了後に現在スキルの継続特化を試みます @@ -1596,6 +1597,86 @@ MAA を複数開くには、新しい MAA を他のフォルダにコピーし 仙術杯選手 本物の仙術だなあ。 「{key=AutoRoguelike}」で、任意のテーマで N15 難易度以上をクリアする + + スーパーバイザー + MAAのログを見つめていると、自動戦闘の成功率が上がるらしい。 + ログフローティングウィンドウを開いた状態でタスクを1回完了する + + 公開採用ギャンブラー + 一か八か、自転車がバイクに。 + 公開採用でタグを50回リフレッシュする + + 手がかり収集家 + ロドス島の社交界の花。 + 手がかり交換を累計20回開く + + 手がかり哲学者 + 集まりの意味とは何か? + 手がかり交換を累計50回開く + + 手がかりシェアラー + 良いものは友達と分かち合う。 + 友達に手がかりを累計20回送る + + 手がかり慈善家 + 持って行って、どういたしまして。 + 友達に手がかりを累計50回送る + + タスクチェーン王 + ワンクリックで起動、全部完了。 + 1回の実行で7個以上のタスクを選択する + + 更新強迫症 + 更新がある?更新する! + 更新リリース後1日以内に更新を完了する + + 更新アーリーバード + いち早く体験! + 更新リリース後1時間以内に更新を完了する + + 理性プランナー + 精密、完璧、寸分の狂いもない。 + {key=Combat}を完了した時、すべての理性をちょうど使い切る + + ホットキーマジシャン + Ctrl + Shift + Alt + Win + F13 + 修飾キーが4つ以上のホットキー組み合わせを設定して使用する + + 倉庫の守銭奴 + 一つも使わせない! + 倉庫認識機能が倉庫内の特定の材料が10000個以上あることを検出する + + 人事部専門員 + どうして全部予備行動チームなんだ! + 1回の公開採用タスクで連続10回採用する + + 人事部上級専門員 + 上級オペレータータグはどこだ?どこだ?🎶 + 1回の公開採用タスクで連続20回採用する + + 私は倉庫管理員! + MAAがMAAを見つけた! + オペレーター認識にオペレーター「パラス」が含まれる + + 404! + このタスクは見つからないようです + リモートプロトコルで間違ったタスクidを呼び出す + + 言語学者 + MAA、しかし国際化。 + インターフェース言語を切り替えてみる + + 起動時起動 + 理論的には、起動時間が少し遅くなった + MAAを起動時に起動するように設定する + + 手がかり強迫症 + 七色戦隊、集合! + 7日連続で毎日手がかりを集めて手がかり交換を開く + + 325 + この数字の意味を知っているでしょう? + 3/13/23時25分にまだMAAを使用している diff --git a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml index e224bdd8a3..b13bbcd81b 100644 --- a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml +++ b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml @@ -122,6 +122,7 @@ 오리지늄 조각 자동 보충 응접실 게시판에서 크레딧 수집 단서 교환 수행 + 단서 교환 시작 단서 보내기 인프라 교대 필요 스킬 훈련 완료 시 이어서 훈련 시도 @@ -1597,6 +1598,86 @@ MAA를 독립된 새 폴더에 압축 해제하거나, MAA에 속하지 않는 D 마스터피스 방금 제 플레이 보셨나요? 이건 기록으로 남겨야 해요! 「{key=AutoRoguelike}」 기능으로 {key=AutoRoguelike} 난이도 15 이상 클리어 + + 슈퍼바이저 + MAA의 로그를 지켜보면 자동 전투 성공률이 효과적으로 향상된다고 합니다. + 로그 플로팅 창을 열고 작업을 1회 완료 + + 공개 모집 도박꾼 + 한 번 해보자, 자전거가 오토바이가 된다. + 공개 모집에서 태그를 50회 새로고침 + + 단서 수집가 + 로도스 아일랜드 사교계의 꽃. + 단서 교환을 누적 20회 열기 + + 단서 철학자 + 모임의 의미는 무엇인가? + 단서 교환을 누적 50회 열기 + + 단서 공유자 + 좋은 것은 친구와 나눠야 해요. + 친구에게 단서를 누적 20회 보내기 + + 단서 자선가 + 가져가세요, 감사 인사는 필요 없어요. + 친구에게 단서를 누적 50회 보내기 + + 작업 체인 왕 + 한 번의 클릭으로 시작, 모두 완료. + 단일 실행에서 7개 이상의 작업 선택 + + 업데이트 강박증 + 업데이트가 있나? 업데이트! + 업데이트 출시 후 1일 이내에 업데이트 완료 + + 업데이트 얼리버드 + 먼저 체험하기! + 업데이트 출시 후 1시간 이내에 업데이트 완료 + + 이성 플래너 + 정밀하고, 완벽하고, 한 푼도 어긋나지 않아요. + {key=Combat} 완료 시 모든 이성을 정확히 소진 + + 단축키 마법사 + Ctrl + Shift + Alt + Win + F13 + 수정 키가 4개 이상인 단축키 조합을 설정하고 사용 + + 창고 수전노 + 하나도 쓰지 말아요! + 창고 인식 기능이 창고 내 특정 재료가 10000개 이상임을 감지 + + 인사부 전문가 + 왜 다 예비 행동팀이에요! + 단일 공개 모집 작업에서 연속 10회 모집 + + 인사부 고급 전문가 + 고급 오퍼레이터 태그는 어디 있나요? 어디 있나요?🎶 + 단일 공개 모집 작업에서 연속 20회 모집 + + 저는 창고 관리원이에요! + MAA가 MAA를 찾았어요! + 오퍼레이터 인식에 오퍼레이터 "팔라스" 포함 + + 404! + 이 작업을 찾을 수 없는 것 같아요 + 원격 프로토콜로 잘못된 작업 id 호출 + + 언어학자 + MAA, 하지만 국제화. + 인터페이스 언어 전환 시도 + + 부팅 시 시작 + 이론적으로 부팅 속도가 조금 느려졌어요 + MAA를 부팅 시 시작하도록 설정 + + 단서 강박증 + 칠색 전대, 집합! + 7일 연속으로 매일 단서를 모아 단서 교환 열기 + + 325 + 이 숫자가 무슨 의미인지 아시죠? + 3/13/23시 25분에 여전히 MAA 사용 중 diff --git a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml index 6b4632b4b3..5f09c596b4 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml @@ -122,6 +122,7 @@ 源石碎片自动补货 会客室信息板收取信用 进行线索交流 + 已开启线索交流 赠送线索 需执行基建换班 训练完成后继续尝试专精当前技能 @@ -1596,6 +1597,86 @@ C:\\leidian\\LDPlayer9。\n 仙术杯选手 仙术牛牛真仙术。 使用 「{key=AutoRoguelike}」 通关任意肉鸽 N15 及以上难度 + + 超级监工 + 据说,盯着MAA的日志能够有效提高代理成功率。 + 开着日志悬浮窗完成一次任务 + + 公招赌徒 + 搏一搏,单车变摩托。 + 在公开招募中刷新标签 50 次 + + 线索搜集 + 罗德岛交际花。 + 累计开启线索交流 20 次 + + 线索哲学家 + 聚会的意义是什么? + 累计开启线索交流 50 次 + + 线索分享 + 好东西要和朋友分享。 + 累计向好友发送 20 次线索 + + 线索慈善家 + 拿去花,不用谢。 + 累计向好友发送 50 次线索 + + 任务链王 + 一键启动,全部搞定。 + 单次运行中,勾选超过 7 个任务 + + 更新强迫症 + 有更新?更! + 在更新发布后 1 天内完成更新 + + 更新尝鲜者 + 抢先体验! + 在更新发布后 1 小时内完成更新 + + 理智规划师 + 精确,完美,分毫不差。 + 完成{key=Combat}时刚好消耗完所有理智 + + 热键魔术师 + Ctrl + Shift + Alt + Win + F13 + 设置并使用一个超过 4 个修饰键的热键组合 + + 仓库守财奴 + 一个都不许用! + 仓库识别功能识别到仓库内某种材料数量超过 10000 个 + + 人事部专员 + 怎么全是预备行动组啊! + 单轮公招任务连续招募 10 次 + + 人事部高级专员 + 高资在哪里啊高资在哪里🎶 + 单轮公招任务连续招募 20 次 + + 我是仓管! + 牛牛找到了牛牛! + 干员识别中包含干员"帕拉斯" + + 404! + 这个任务似乎找不到了 + 使用远程协议调用错误的任务 id + + 语言学家 + MAA,但国际化。 + 尝试切换界面语言 + + 开机启动 + 理论上你的开机速度又慢了一点 + 设置开机启动 MAA + + 线索强迫症 + 七色战队,集合! + 连续 7 天每天集齐并开启线索交流 + + 325 + 你知道这个数字是什么意思对吧 + 在 3/13/23 点 25 分还在使用 MAA diff --git a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml index fd3345b108..54d6581b2c 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml @@ -122,6 +122,7 @@ 源石碎片自動補貨 會客室資訊板收取信用 進行線索交流 + 已開啟線索交流 贈送線索 需執行基建換班 訓練完成後繼續嘗試專精目前技能 @@ -1596,6 +1597,86 @@ C:\\leidian\\LDPlayer9。\n 仙術杯選手 仙術牛牛真仙術。 使用 「{key=AutoRoguelike}」 通關任意肉鴿 N15 及以上難度 + + 超級監工 + 據說,盯著MAA的日誌能夠有效提高代理成功率。 + 開著日誌懸浮窗完成一次任務 + + 公招賭徒 + 搏一搏,單車變摩托。 + 在公開招募中刷新標籤 50 次 + + 線索搜集 + 羅德島交際花。 + 累計開啟線索交流 20 次 + + 線索哲學家 + 聚會的意義是什麼? + 累計開啟線索交流 50 次 + + 線索分享 + 好東西要和朋友分享。 + 累計向好友發送 20 次線索 + + 線索慈善家 + 拿去花,不用謝。 + 累計向好友發送 50 次線索 + + 任務鏈王 + 一鍵啟動,全部搞定。 + 單次運行中,勾選超過 7 個任務 + + 更新強迫症 + 有更新?更! + 在更新發布後 1 天內完成更新 + + 更新嘗鮮者 + 搶先體驗! + 在更新發布後 1 小時內完成更新 + + 理智規劃師 + 精確,完美,分毫不差。 + 完成{key=Combat}時剛好消耗完所有理智 + + 熱鍵魔術師 + Ctrl + Shift + Alt + Win + F13 + 設置並使用一個超過 4 個修飾鍵的熱鍵組合 + + 倉庫守財奴 + 一個都不許用! + 倉庫識別功能識別到倉庫內某種材料數量超過 10000 個 + + 人事部專員 + 怎麼全是預備行動組啊! + 單輪公招任務連續招募 10 次 + + 人事部高級專員 + 高資在哪裡啊高資在哪裡🎶 + 單輪公招任務連續招募 20 次 + + 我是倉管! + 牛牛找到了牛牛! + 幹員識別中包含幹員"帕拉斯" + + 404! + 這個任務似乎找不到了 + 使用遠程協議調用錯誤的任務 id + + 語言學家 + MAA,但國際化。 + 嘗試切換界面語言 + + 開機啟動 + 理論上你的開機速度又慢了一點 + 設置開機啟動 MAA + + 線索強迫症 + 七色戰隊,集合! + 連續 7 天每天集齊並開啟線索交流 + + 325 + 你知道這個數字是什麼意思對吧 + 在 3/13/23 點 25 分還在使用 MAA diff --git a/src/MaaWpfGui/Services/RemoteControl/RemoteControlService.cs b/src/MaaWpfGui/Services/RemoteControl/RemoteControlService.cs index 959aaf1246..7b00786476 100644 --- a/src/MaaWpfGui/Services/RemoteControl/RemoteControlService.cs +++ b/src/MaaWpfGui/Services/RemoteControl/RemoteControlService.cs @@ -153,10 +153,7 @@ public class RemoteControlService private static T GetPrivateFieldValue(object instance, string fieldName) { - if (instance == null) - { - throw new ArgumentNullException(nameof(instance)); - } + ArgumentNullException.ThrowIfNull(instance); if (string.IsNullOrEmpty(fieldName)) { @@ -164,13 +161,7 @@ public class RemoteControlService } Type type = instance.GetType(); - FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); - - if (fieldInfo == null) - { - throw new ArgumentException($"Field '{fieldName}' not found in type '{type.FullName}'."); - } - + FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new ArgumentException($"Field '{fieldName}' not found in type '{type.FullName}'."); if (!typeof(T).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException($"Field '{fieldName}' is not of type {typeof(T)}."); @@ -181,10 +172,7 @@ public class RemoteControlService private static void InvokeInstanceMethod(object instance, string methodName) { - if (instance == null) - { - throw new ArgumentNullException(nameof(instance)); - } + ArgumentNullException.ThrowIfNull(instance); if (string.IsNullOrEmpty(methodName)) { @@ -192,22 +180,13 @@ public class RemoteControlService } Type type = instance.GetType(); - MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); - - if (methodInfo == null) - { - throw new ArgumentException($"Method '{methodName}' not found in type '{type.FullName}'."); - } - + MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new ArgumentException($"Method '{methodName}' not found in type '{type.FullName}'."); methodInfo.Invoke(instance, null); } private static T InvokeInstanceFunction(object instance, string methodName) { - if (instance == null) - { - throw new ArgumentNullException(nameof(instance)); - } + ArgumentNullException.ThrowIfNull(instance); if (string.IsNullOrEmpty(methodName)) { @@ -215,13 +194,7 @@ public class RemoteControlService } Type type = instance.GetType(); - MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); - - if (methodInfo == null) - { - throw new ArgumentException($"Method '{methodName}' not found in type '{type.FullName}'."); - } - + MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new ArgumentException($"Method '{methodName}' not found in type '{type.FullName}'."); if (!typeof(T).IsAssignableFrom(methodInfo.ReturnType)) { throw new ArgumentException($"Method '{methodName}' is not {typeof(T)}."); @@ -232,10 +205,7 @@ public class RemoteControlService private static async Task InvokeInstanceAsyncFunction(object instance, string methodName) { - if (instance == null) - { - throw new ArgumentNullException(nameof(instance)); - } + ArgumentNullException.ThrowIfNull(instance); if (string.IsNullOrEmpty(methodName)) { @@ -243,12 +213,7 @@ public class RemoteControlService } Type type = instance.GetType(); - MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); - - if (methodInfo == null) - { - throw new ArgumentException($"Method '{methodName}' not found in type '{type.FullName}'."); - } + MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new ArgumentException($"Method '{methodName}' not found in type '{type.FullName}'."); // 检查方法是否是异步方法 (返回Task或Task) if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType)) @@ -261,10 +226,7 @@ public class RemoteControlService private static TResult InvokeStaticFunction(Type staticType, string methodName) { - if (staticType == null) - { - throw new ArgumentNullException(nameof(staticType)); - } + ArgumentNullException.ThrowIfNull(staticType); if (string.IsNullOrEmpty(methodName)) { @@ -273,12 +235,9 @@ public class RemoteControlService MethodInfo methodInfo = staticType.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static); - if (methodInfo == null) - { - throw new ArgumentException($"Method '{methodName}' not found in type '{staticType.FullName}'."); - } - - return (TResult)methodInfo.Invoke(null, null); + return methodInfo == null + ? throw new ArgumentException($"Method '{methodName}' not found in type '{staticType.FullName}'.") + : (TResult)methodInfo.Invoke(null, null); } #endregion @@ -348,6 +307,9 @@ public class RemoteControlService case "StopTask": _instantTaskQueue.Enqueue(task); break; + default: + AchievementTrackerHelper.Instance.Unlock(AchievementIds.NotFound404); + break; } } } @@ -459,11 +421,8 @@ public class RemoteControlService TaskQueueViewModel.FightTask.Stage1 = data; }); break; - - // ReSharper disable once RedundantEmptySwitchSection default: - // 未知的Type统一直接发给MAACore - // No! 未知的任务一概不处理 + AchievementTrackerHelper.Instance.Unlock(AchievementIds.NotFound404); break; } @@ -555,10 +514,8 @@ public class RemoteControlService break; } - // ReSharper disable once RedundantEmptySwitchSection default: - // 未知的Type统一直接发给MAACore - // No! 未知的任务一概不处理 + AchievementTrackerHelper.Instance.Unlock(AchievementIds.NotFound404); break; } diff --git a/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs b/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs index 090a50286d..71b74a0e4e 100644 --- a/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs @@ -641,6 +641,11 @@ public class TaskQueueViewModel : Screen _lastTimerElapsed = currentTime; + if ((currentTime.Hour == 3 || currentTime.Hour == 13 || currentTime.Hour == 23) && currentTime.Minute == 25 && !Idle) + { + AchievementTrackerHelper.Instance.Unlock(AchievementIds.Time325); + } + VersionUpdateSettingsUserControlModel.Instance.RefreshMirrorChyanCdkRemaining(); HandleDatePromptUpdate(); HandleCheckForUpdates(); @@ -1641,6 +1646,8 @@ public class TaskQueueViewModel : Screen return; } + AchievementTrackerHelper.Instance.SetProgress(AchievementIds.TaskChainKing, count); + taskRet &= Instances.AsstProxy.AsstStart(); if (taskRet) diff --git a/src/MaaWpfGui/ViewModels/UI/ToolboxViewModel.cs b/src/MaaWpfGui/ViewModels/UI/ToolboxViewModel.cs index fbc5ebebb2..4ce6afe5e7 100644 --- a/src/MaaWpfGui/ViewModels/UI/ToolboxViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/ToolboxViewModel.cs @@ -489,20 +489,35 @@ public class ToolboxViewModel : Screen foreach (var item in sortedItems) { var id = (string?)item["id"]; - if (id == null) + if (string.IsNullOrEmpty(id)) { continue; } + int count = -1; + bool hasValidCount = false; + + if (item["have"] != null) + { + var haveValue = item["have"]?.ToString(); + hasValidCount = int.TryParse(haveValue, out count); + } + + string countStr = hasValidCount ? count.FormatNumber(false) : "-1"; + DepotResultDate result = new() { Id = id, Name = ItemListHelper.GetItemName(id), Image = ItemListHelper.GetItemImage(id), - Count = item["have"] != null && int.TryParse(item["have"]?.ToString() ?? "-1", out int haveValue) - ? haveValue.FormatNumber(false) - : "-1", + Count = countStr, }; + if (hasValidCount && count > 0 && + count > AchievementTrackerHelper.Instance.GetProgress(AchievementIds.WarehouseMiser)) + { + AchievementTrackerHelper.Instance.SetProgress(AchievementIds.WarehouseMiser, count); + } + DepotResult.Add(result); } @@ -695,6 +710,11 @@ public class ToolboxViewModel : Screen if (ids.Contains(id)) { OperBoxHaveList.Add(new Operator(id, name, oper.Rarity)); + + if (id == "char_485_pallas") + { + AchievementTrackerHelper.Instance.Unlock(AchievementIds.WarehouseKeeper); + } } else { @@ -733,6 +753,10 @@ public class ToolboxViewModel : Screen { var name = DataHelper.GetLocalizedCharacterName(DataHelper.Operators.FirstOrDefault(i => i.Key == oper.Id).Value) ?? "???"; OperBoxHaveList.Add(new Operator(oper.Id, name, oper.Rarity)); + if (oper.Id == "char_485_pallas") + { + AchievementTrackerHelper.Instance.Unlock(AchievementIds.WarehouseKeeper); + } } } diff --git a/src/MaaWpfGui/ViewModels/UserControl/Settings/GuiSettingsUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/Settings/GuiSettingsUserControlModel.cs index 7a6f4ad7ee..62c0a16c01 100644 --- a/src/MaaWpfGui/ViewModels/UserControl/Settings/GuiSettingsUserControlModel.cs +++ b/src/MaaWpfGui/ViewModels/UserControl/Settings/GuiSettingsUserControlModel.cs @@ -339,8 +339,9 @@ public class GuiSettingsUserControlModel : PropertyChangedBase // var backup = _language; ConfigurationHelper.SetGlobalValue(ConfigurationKeys.Localization, value); - var mainWindow = Application.Current.MainWindow; + AchievementTrackerHelper.Instance.Unlock(AchievementIds.Linguist); + var mainWindow = Application.Current.MainWindow; if (mainWindow != null) { mainWindow.Show(); diff --git a/src/MaaWpfGui/ViewModels/UserControl/Settings/StartSettingsUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/Settings/StartSettingsUserControlModel.cs index 39cd9ed18c..2c5866aea1 100644 --- a/src/MaaWpfGui/ViewModels/UserControl/Settings/StartSettingsUserControlModel.cs +++ b/src/MaaWpfGui/ViewModels/UserControl/Settings/StartSettingsUserControlModel.cs @@ -71,6 +71,7 @@ public class StartSettingsUserControlModel : PropertyChangedBase } SetAndNotify(ref _startSelf, value); + AchievementTrackerHelper.Instance.Unlock(AchievementIds.StartupBoot); } } diff --git a/src/MaaWpfGui/Views/UI/AchievementListWindow.xaml b/src/MaaWpfGui/Views/UI/AchievementListWindow.xaml index c0b87be0b2..94b5369915 100644 --- a/src/MaaWpfGui/Views/UI/AchievementListWindow.xaml +++ b/src/MaaWpfGui/Views/UI/AchievementListWindow.xaml @@ -9,7 +9,7 @@ Title="{DynamicResource AchievementList}" Width="600" Height="400" - MinWidth="250" + MinWidth="300" Background="{DynamicResource MdXamlBackground}"> - + + + + + - + - + + + + + + + + + + + + + + + - + diff --git a/src/MaaWpfGui/Views/UI/ToolboxView.xaml b/src/MaaWpfGui/Views/UI/ToolboxView.xaml index 901eae78a8..7e45d8ddcc 100644 --- a/src/MaaWpfGui/Views/UI/ToolboxView.xaml +++ b/src/MaaWpfGui/Views/UI/ToolboxView.xaml @@ -385,7 +385,7 @@ VerticalAlignment="Center" Command="{s:Action StartDepot}" Content="{DynamicResource StartToDepotRecognition}" - IsEnabled="{Binding 'Idle and Inited'}" /> + IsEnabled="{c:Binding 'Idle and Inited'}" /> diff --git a/src/MaaWpfGui/Views/UserControl/HotKeyEditorUserControl.xaml b/src/MaaWpfGui/Views/UserControl/HotKeyEditorUserControl.xaml index 2cd6293531..31e933151d 100644 --- a/src/MaaWpfGui/Views/UserControl/HotKeyEditorUserControl.xaml +++ b/src/MaaWpfGui/Views/UserControl/HotKeyEditorUserControl.xaml @@ -5,11 +5,11 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Name="UserControl" - d:DesignWidth="180" + d:DesignWidth="200" mc:Ignorable="d"> = 4) + { + AchievementTrackerHelper.Instance.Unlock(AchievementIds.HotkeyMagician); + } + // Update the value HotKey = new MaaHotKey(key, modifiers); }