diff --git a/docs/en-us/protocol/callback-schema.md b/docs/en-us/protocol/callback-schema.md index d809fe0adc..e64cb35b1e 100644 --- a/docs/en-us/protocol/callback-schema.md +++ b/docs/en-us/protocol/callback-schema.md @@ -524,39 +524,8 @@ Todo ```json // Corresponding details field example - "done": bool, // Whether recognition is complete, false means still in progress (data during process) - "arkplanner": { // https://penguin-stats.cn/planner - "object": { - "items": [ - { - "id": "2004", - "have": 4, - "name": "高级作战记录" // "Advanced Battle Record" - }, - { - "id": "mod_unlock_token", - "have": 25, - "name": "模组数据块" // "Module Data Block" - }, - { - "id": "2003", - "have": 20, - "name": "中级作战记录" // "Tactical Battle Record" - } - ], - "@type": "@penguin-statistics/depot" - }, - "data": "{\"@type\":\"@penguin-statistics/depot\",\"items\":[{\"id\":\"2004\",\"have\":4,\"name\":\"高级作战记录\"},{\"id\":\"mod_unlock_token\",\"have\":25,\"name\":\"模组数据块\"},{\"id\":\"2003\",\"have\":20,\"name\":\"中级作战记录\"}]}" - }, - "lolicon": { // https://arkntools.app/#/material - "object": { - "2004" : 4, - "mod_unlock_token": 25, - "2003": 20 - }, - "data": "{\"2003\":20,\"2004\": 4,\"mod_unlock_token\": 25}" - } - // Currently only supports ArkPlanner and Lolicon (Arkntools) formats, may support more websites in the future + "done": bool, // Whether recognition is complete, false means still in progress (data during process) + "data": "{\"2001\":18000,\"31043\":317}" // JSON string, format: {"itemId": quantity, ...} ``` - `OperBox` diff --git a/docs/ja-jp/protocol/callback-schema.md b/docs/ja-jp/protocol/callback-schema.md index 6c4f3f047e..b8510e2806 100644 --- a/docs/ja-jp/protocol/callback-schema.md +++ b/docs/ja-jp/protocol/callback-schema.md @@ -535,44 +535,13 @@ Todo } ``` -- `DepotInfo` +- `Depot` 倉庫のアイテムの認識結果 ```json // 対応する詳細フィールドの例 - "done": bool, // 認識が完了したかどうか,false はまだ進行中かどうか(処理中のデータ) - "arkplanner": { // https://penguin-stats.cn/planner - "object": { - "items": [ - { - "id": "2004", - "have": 4, - "name": "上級作戦記録" - }, - { - "id": "mod_unlock_token", - "have": 25, - "name": "モジュールデータ" - }, - { - "id": "2003", - "have": 20, - "name": "中級作戦記録" - } - ], - "@type": "@penguin-statistics/depot" - }, - "data": "{\"@type\":\"@penguin-statistics/depot\",\"items\":[{\"id\":\"2004\",\"have\":4,\"name\":\"上級作戦記録\"},{\"id\":\"mod_unlock_token\",\"have\":25,\"name\":\"モジュールデータ\"},{\"id\":\"2003\",\"have\":20,\"name\":\"中級作戦記録\"}]}" - }, - "lolicon": { // https://arkntools.app/#/material - "object": { - "2004" : 4, - "mod_unlock_token": 25, - "2003": 20 - }, - "data": "{\"2003\":20,\"2004\": 4,\"mod_unlock_token\": 25}" - } - // 現在は ArkPlanner と Lolicon (Arkntools) 形式のみ対応、今後対応するサイトが増える可能性あり + "done": bool, // 認識が完了したかどうか,false はまだ進行中かどうか(処理中のデータ) + "data": "{\"2001\":18000,\"31043\":317}" // JSON文字列、形式: {"アイテムID": 数量, ...} ``` - `OperBoxInfo` diff --git a/docs/ko-kr/protocol/callback-schema.md b/docs/ko-kr/protocol/callback-schema.md index b55ff2a654..406f54e582 100644 --- a/docs/ko-kr/protocol/callback-schema.md +++ b/docs/ko-kr/protocol/callback-schema.md @@ -474,8 +474,7 @@ typedef void(ASST_CALL* AsstCallback)(int msg, const char* details, void* custom - `Depot` 창고 인식 결과. `details` 필드 구조는 다음과 같습니다: - `done` (boolean, required): 인식 완료 여부, `false`는 아직 인식 중임(진행 중 데이터)을 의미 - - `arkplanner` (object, required): [ArkPlanner](https://penguin-stats.cn/planner) 포맷 데이터 - - `lolicon` (object, required): [lolicon](https://arkntools.app/#/material) (Arkntools) 포맷 데이터 + - `data` (string, required): JSON 문자열, 형식은 `{"아이템ID": 수량, ...}`, 예: `{"2001":18000,"31043":317}` - `OperBox` 오퍼레이터 보관함 인식 결과. `details` 필드 구조는 다음과 같습니다: diff --git a/docs/zh-cn/protocol/callback-schema.md b/docs/zh-cn/protocol/callback-schema.md index 175630b698..2ed9b68626 100644 --- a/docs/zh-cn/protocol/callback-schema.md +++ b/docs/zh-cn/protocol/callback-schema.md @@ -474,8 +474,7 @@ typedef void(ASST_CALL* AsstCallback)(int msg, const char* details, void* custom - `Depot` 仓库识别结果。`details` 字段结构如下: - `done` (boolean, required): 是否已经识别完了,为 `false` 表示仍在识别中(过程中的数据)。 - - `arkplanner` (object, required): [ArkPlanner](https://penguin-stats.cn/planner) 格式的数据。 - - `lolicon` (object, required): [lolicon](https://arkntools.app/#/material) (Arkntools) 格式的数据。 + - `data` (string, required): JSON 字符串,格式为 `{"物品ID": 数量, ...}`,例如 `{"2001":18000,"31043":317}`。 - `OperBox` 干员识别结果。`details` 字段结构如下: diff --git a/docs/zh-tw/protocol/callback-schema.md b/docs/zh-tw/protocol/callback-schema.md index 1c06ca4ad6..6dd25322b3 100644 --- a/docs/zh-tw/protocol/callback-schema.md +++ b/docs/zh-tw/protocol/callback-schema.md @@ -536,39 +536,8 @@ Todo ```json // 對應的 details 欄位舉例 - "done": bool, // 是否已經辨識完了,為 false 表示仍在辨識中(過程中的數據) - "arkplanner": { // https://penguin-stats.io/planner - "object": { - "items": [ - { - "id": "2004", - "have": 4, - "name": "高級作戰記錄" - }, - { - "id": "mod_unlock_token", - "have": 25, - "name": "模組數據塊" - }, - { - "id": "2003", - "have": 20, - "name": "中級作戰記錄" - } - ], - "@type": "@penguin-statistics/depot" - }, - "data": "{\"@type\":\"@penguin-statistics/depot\",\"items\":[{\"id\":\"2004\",\"have\":4,\"name\":\"高級作戰記錄\"},{\"id\":\"mod_unlock_token\",\"have\":25,\"name\":\"模組數據塊\"},{\"id\":\"2003\",\"have\":20,\"name\":\"中級作戰記錄\"}]}" - }, - "lolicon": { // https://arkntools.app/#/material - "object": { - "2004" : 4, - "mod_unlock_token": 25, - "2003": 20 - }, - "data": "{\"2003\":20,\"2004\": 4,\"mod_unlock_token\": 25}" - } - // 目前只支援 ArkPlanner 和 Lolicon (Arkntools) 的格式,以後可能會兼容更多網站 + "done": bool, // 是否已經辨識完了,為 false 表示仍在辨識中(過程中的數據) + "data": "{\"2001\":18000,\"31043\":317}" // JSON 字串,格式為 {"物品ID": 數量, ...} ``` - `OperBox` diff --git a/resource/config.json b/resource/config.json index 4dc3dea48d..7f979d15e5 100644 --- a/resource/config.json +++ b/resource/config.json @@ -36,11 +36,6 @@ "recruitUrl": "https://backend.yituliu.cn/maa/upload/recruit", "dropUrl": "https://backend.yituliu.cn/maa/upload/stageDrop" }, - "depotExportTemplate": { - "Doc": "仓库识别导出结果模板", - "arkPlanner": "{\"@type\": \"@penguin-statistics/depot\",\"items\": []}", - "arkPlanner_Doc": "https://penguin-stats.cn/planner" - }, "debug": { "cleanFilesFreq": 5, "maxDebugFileNum": 10 diff --git a/src/MaaCore/Config/GeneralConfig.cpp b/src/MaaCore/Config/GeneralConfig.cpp index 3aa7760623..5a10c16b76 100644 --- a/src/MaaCore/Config/GeneralConfig.cpp +++ b/src/MaaCore/Config/GeneralConfig.cpp @@ -48,8 +48,6 @@ bool asst::GeneralConfig::parse(const json::value& json) m_options.yituliu_report.drop_url = yituliu_opt->get("dropUrl", std::string()); m_options.yituliu_report.recruit_url = yituliu_opt->get("recruitUrl", std::string()); } - m_options.depot_export_template.ark_planner = - options_json.get("depotExportTemplate", "arkPlanner", std::string()); m_options.debug.clean_files_freq = options_json.get("debug", "cleanFilesFreq", 50); m_options.debug.max_debug_file_num = options_json.get("debug", "maxDebugFileNum", 100); } diff --git a/src/MaaCore/Config/GeneralConfig.h b/src/MaaCore/Config/GeneralConfig.h index e69f3f859a..749649bd5d 100644 --- a/src/MaaCore/Config/GeneralConfig.h +++ b/src/MaaCore/Config/GeneralConfig.h @@ -54,7 +54,6 @@ struct Options int swipe_with_pause_required_distance = 0; std::vector minitouch_programs_order; RequestInfo penguin_report; // 企鹅物流汇报:每次到结算界面,汇报掉落数据至企鹅物流 https://penguin-stats.io - DepotExportTemplate depot_export_template; // 仓库识别结果导出模板 RequestInfo yituliu_report; // 一图流大数据汇报:目前只有公招功能,https://ark.yituliu.cn/survey/maarecruitdata DebugConf debug; }; diff --git a/src/MaaCore/Task/Miscellaneous/DepotRecognitionTask.cpp b/src/MaaCore/Task/Miscellaneous/DepotRecognitionTask.cpp index 71d918723d..e3161b4bf7 100644 --- a/src/MaaCore/Task/Miscellaneous/DepotRecognitionTask.cpp +++ b/src/MaaCore/Task/Miscellaneous/DepotRecognitionTask.cpp @@ -58,38 +58,18 @@ void asst::DepotRecognitionTask::callback_analyze_result(bool done) { LogTraceFunction; - auto& templ = Config.get_options().depot_export_template; json::value info = basic_info_with_what("DepotInfo"); auto& details = info["details"]; - // https://penguin-stats.io/planner - if (auto arkplanner_template_opt = json::parse(templ.ark_planner)) { - auto& arkplanner = details["arkplanner"]; - auto& arkplanner_obj = arkplanner["object"]; - arkplanner_obj = arkplanner_template_opt.value(); - auto& arkplanner_data_items = arkplanner_obj["items"]; - - for (const auto& [item_id, item_info] : m_all_items) { - arkplanner_data_items.emplace( - json::object { - { "id", item_id }, - { "have", item_info.quantity }, - { "name", item_info.item_name }, - }); - } - arkplanner["data"] = arkplanner_obj.to_string(); + // 简化格式:只保留 done 和 data + // data 为 {"itemId": count} 格式 + json::object data_obj; + for (const auto& [item_id, item_info] : m_all_items) { + data_obj.emplace(item_id, item_info.quantity); } - // https://arkntools.app/#/material - { - auto& lolicon = details["lolicon"]; - auto& lolicon_obj = lolicon["object"]; - for (const auto& [item_id, item_info] : m_all_items) { - lolicon_obj.emplace(item_id, item_info.quantity); - } - lolicon["data"] = lolicon_obj.to_string(); - } details["done"] = done; + details["data"] = data_obj.to_string(); callback(AsstMsg::SubTaskExtraInfo, info); } diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs index 097b03ae35..05fff48ecd 100644 --- a/src/MaaWpfGui/Main/AsstProxy.cs +++ b/src/MaaWpfGui/Main/AsstProxy.cs @@ -1721,7 +1721,7 @@ public class AsstProxy switch (taskChain) { case "Depot": - Instances.ToolboxViewModel.DepotParse((JObject?)subTaskDetails); + Instances.ToolboxViewModel.DepotParse((JObject?)subTaskDetails, updateSyncTime: true); break; case "OperBox": @@ -1785,6 +1785,9 @@ public class AsstProxy AchievementTrackerHelper.Instance.AddProgressToGroup(AchievementIds.SanitySpenderGroup, curTimes > 0 ? curTimes : 1); + // 联动更新 Depot 数据 + Instances.ToolboxViewModel.UpdateDepotFromDrops(drops); + break; } diff --git a/src/MaaWpfGui/Res/Localizations/en-us.xaml b/src/MaaWpfGui/Res/Localizations/en-us.xaml index 11a554b53e..d73bb9dc85 100644 --- a/src/MaaWpfGui/Res/Localizations/en-us.xaml +++ b/src/MaaWpfGui/Res/Localizations/en-us.xaml @@ -726,7 +726,7 @@ Do not adjust the proxy multiplier setting in the game Minimum Requirements:  Days left open:  Inventory - Inventory data can be updated via 「{key=Toolbox}-{key=DepotRecognition}」 + Inventory data can be updated via 「{key=Toolbox}-{key=DepotRecognition}」 Less than 1 day 1-7 CE-6 @@ -809,6 +809,7 @@ Right Click: Select Target Process Depot {key=DepotRecognition} + Last Sync Time This function is still in testing. If you encounter any unexpected results, please zip the 「debug/depot」 folder in the installation path and submit an issue on GitHub Start to Identify Export to Arkplanner diff --git a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml index 87df9b8064..5474148427 100644 --- a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml +++ b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml @@ -810,6 +810,7 @@ C:\\leidian\\LDPlayer9 倉庫アイテム認識 {key=DepotRecognition} + 前回同期時間 この機能はまだテスト版です。エラーが発生/統計情報に問題があれば、debug/depotフォルダを圧縮し、issueを提出してください~ 認識開始 Arkplannerへ出力 diff --git a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml index 03c63509cf..6bea20fcae 100644 --- a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml +++ b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml @@ -811,6 +811,7 @@ C:\\leidian\\LDPlayer9 창고 인식 {key=DepotRecognition} + 마지막 동기화 시간 이 기능은 현재 테스트 중입니다. 내보내기 전 인식 결과가 맞는지 확인 후 사용하세요\n만약 오류가 있다면 MAA 경로 내 debug 폴더 안의 depot 폴더 전체를 압축해서 Github의 issue로 올려주세요 인식 시작 Arkplanner로 내보내기 diff --git a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml index 4daec0f375..a0f63f9f48 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml @@ -810,6 +810,7 @@ C:\\leidian\\LDPlayer9。\n 仓库识别 {key=DepotRecognition} + 上次同步时间 该功能尚处于测试阶段,请检查结果是否准确再行使用。若有误,欢迎打包 debug/depot 文件夹后向我们提交 issue ~ 开始识别 导出至企鹅物流刷图规划 diff --git a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml index 5e36b98a12..b456e6a4a1 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml @@ -810,6 +810,7 @@ C:\\leidian\\LDPlayer9。\n 倉庫辨識 {key=DepotRecognition} + 上次同步時間 該功能尚處於測試階段,請檢查結果是否準確再行使用。若有誤,歡迎打包 debug/depot 資料夾後向我們提交 issue ~ 開始辨識 匯出到企鵝物流刷圖規劃 diff --git a/src/MaaWpfGui/Services/StageManager.cs b/src/MaaWpfGui/Services/StageManager.cs index a43ac3fab5..24ba61bd42 100644 --- a/src/MaaWpfGui/Services/StageManager.cs +++ b/src/MaaWpfGui/Services/StageManager.cs @@ -689,7 +689,7 @@ public class StageManager if (!string.IsNullOrEmpty(stage.Drop)) { var str = $"{stage.Value}: {ItemListHelper.GetItemName(stage.Drop) ?? stage.Drop}"; - var count = Instances.ToolboxViewModel?.DepotResult?.FirstOrDefault(d => d.Id == stage.Drop)?.Count; + var count = Instances.ToolboxViewModel?.DepotResult?.FirstOrDefault(d => d.Id == stage.Drop)?.DisplayCount; if (!string.IsNullOrEmpty(count) && count != "-1") { str += $" ({LocalizationHelper.GetString("Inventory")} {count})"; diff --git a/src/MaaWpfGui/ViewModels/UI/ToolboxViewModel.cs b/src/MaaWpfGui/ViewModels/UI/ToolboxViewModel.cs index 6af14a35e5..74acf8bca4 100644 --- a/src/MaaWpfGui/ViewModels/UI/ToolboxViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/ToolboxViewModel.cs @@ -16,6 +16,7 @@ using System; using System.Buffers; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -32,6 +33,7 @@ using MaaWpfGui.Main; using MaaWpfGui.Models; using MaaWpfGui.Models.AsstTasks; using MaaWpfGui.States; +using MaaWpfGui.Utilities; using MaaWpfGui.Utilities.ValueType; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -54,6 +56,7 @@ public class ToolboxViewModel : Screen /// public ToolboxViewModel() { + PropertyDependsOnUtility.InitializePropertyDependencies(this); DisplayName = LocalizationHelper.GetString("Toolbox"); _runningState = RunningState.Instance; _runningState.StateChanged += (__, e) => { @@ -406,6 +409,35 @@ public class ToolboxViewModel : Screen set => SetAndNotify(ref _depotInfo, value); } + private DateTime? _lastDepotSyncTime; + + /// + /// Gets or sets 上次仓库同步时间(UTC 时间) + /// + public DateTime? LastDepotSyncTime + { + get => _lastDepotSyncTime; + set => SetAndNotify(ref _lastDepotSyncTime, value); + } + + /// + /// Gets 上次仓库同步时间的显示文本(本地时间) + /// + [PropertyDependsOn(nameof(LastDepotSyncTime))] + public string LastDepotSyncTimeText + { + get { + if (LastDepotSyncTime == null) + { + return string.Empty; + } + + // 将 UTC 时间转换为本地时间显示 + var localTime = LastDepotSyncTime.Value.ToLocalTime(); + return localTime.ToString(); + } + } + private ObservableCollection _depotResult = []; /// @@ -414,7 +446,25 @@ public class ToolboxViewModel : Screen public ObservableCollection DepotResult { get => _depotResult; - set => SetAndNotify(ref _depotResult, value); + set { + SetAndNotify(ref _depotResult, value); + InvalidateDepotCache(); + } + } + + // 缓存相关字段 + private bool _depotCacheInvalid = true; + private string? _cachedArkPlannerResult; + private string? _cachedLoliconResult; + + /// + /// 标记仓库缓存失效 + /// + private void InvalidateDepotCache() + { + _depotCacheInvalid = true; + _cachedArkPlannerResult = null; + _cachedLoliconResult = null; } public class DepotResultDate @@ -425,54 +475,135 @@ public class ToolboxViewModel : Screen public BitmapSource? Image { get; set; } - public string? Count { get; set; } + /// + /// Gets or sets 物品数量(原始数值) + /// + public int Count { get; set; } + + /// + /// Gets 格式化后的显示数量(用于 UI 绑定) + /// + public string DisplayCount => Count >= 0 ? Count.FormatNumber(false) : "-1"; } - private void SaveDepotDetails(JObject details) + /// + /// 保存仓库详情数据 + /// + private void SaveDepotDetails() { - // var json = details.ToString(Formatting.None); - // ConfigurationHelper.SetValue(ConfigurationKeys.DepotResult, json); + // 构建简化格式:{"itemId": count} + var depotData = new JObject(); + foreach (var item in DepotResult) + { + if (item.Count >= 0) + { + depotData[item.Id] = item.Count; + } + } + + var details = new JObject { + ["done"] = true, + ["data"] = depotData.ToString(Formatting.None), + }; + + // 保存同步时间为 UTC(如果有) + if (LastDepotSyncTime.HasValue) + { + details["syncTime"] = LastDepotSyncTime.Value.ToString("o"); // ISO 8601 格式 + } + JsonDataHelper.Set(JsonDataKey.DepotData, details); } private void LoadDepotDetails() { - // TODO: 删除老数据节省 gui.json 的大小,后续版本可以删除 - // var json = ConfigurationHelper.GetValue(ConfigurationKeys.DepotResult, string.Empty); - ConfigurationHelper.DeleteValue(ConfigurationKeys.DepotResult); var json = JsonDataHelper.Get(JsonDataKey.DepotData, string.Empty); if (string.IsNullOrWhiteSpace(json)) { return; } - try - { - var details = JObject.Parse(json); - DepotParse(details); - } - catch - { - // 兼容老数据或异常时忽略 + var details = JObject.Parse(json); + DepotParse(details); + } + + /// + /// Gets 获取 ArkPlanner 导出格式(带缓存) + /// + public string ArkPlannerResult + { + get { + if (DepotResult.Count == 0) + { + return string.Empty; + } + + // 使用缓存 + if (!_depotCacheInvalid && _cachedArkPlannerResult != null) + { + return _cachedArkPlannerResult; + } + + // 重新计算 + var items = DepotResult + .Where(item => item.Count >= 0) + .Select(item => new JObject { + ["id"] = item.Id, + ["have"] = item.Count, + ["name"] = item.Name ?? string.Empty, + }); + + var result = new JObject { + ["@type"] = "@penguin-statistics/depot", + ["items"] = new JArray(items), + }; + + _cachedArkPlannerResult = result.ToString(Formatting.None); + _depotCacheInvalid = false; // 标记缓存已更新 + return _cachedArkPlannerResult; } } /// - /// Gets or sets the ArkPlanner result. + /// Gets 获取 工具箱 导出格式(带缓存) /// - public string ArkPlannerResult { get; set; } = string.Empty; + public string LoliconResult + { + get { + if (DepotResult.Count == 0) + { + return string.Empty; + } + + // 使用缓存 + if (!_depotCacheInvalid && _cachedLoliconResult != null) + { + return _cachedLoliconResult; + } + + // 重新计算 + var depotData = new JObject(); + foreach (var item in DepotResult) + { + if (item.Count >= 0) + { + depotData[item.Id] = item.Count; + } + } + + _cachedLoliconResult = depotData.ToString(Formatting.None); + _depotCacheInvalid = false; // 标记缓存已更新 + return _cachedLoliconResult; + } + } /// - /// Gets or sets the Lolicon result. + /// 解析仓库识别结果(兼容新旧格式) /// - public string LoliconResult { get; set; } = string.Empty; - - /// - /// parse of depot recognition result - /// - /// detailed json-style parameters - /// Success or not - public bool DepotParse(JObject? details) + /// 详细的 JSON 参数 + /// 是否更新同步时间为当前时间(从 Core 获取新数据时为 true,从本地加载时为 false) + /// 是否成功 + public bool DepotParse(JObject? details, bool updateSyncTime = false) { if (details == null) { @@ -481,38 +612,68 @@ public class ToolboxViewModel : Screen DepotResult.Clear(); - var sortedItems = details["arkplanner"]?["object"]?["items"] - ?.Cast() - .OrderBy(item => (string?)item["id"]) - ?? Enumerable.Empty(); + Dictionary depotItems = []; - foreach (var item in sortedItems) + // 尝试解析新格式 + var dataStr = details["data"]?.ToString(); + if (!string.IsNullOrEmpty(dataStr)) { - var id = (string?)item["id"]; - if (string.IsNullOrEmpty(id)) + try { - continue; + var dataObj = JObject.Parse(dataStr); + foreach (var prop in dataObj.Properties()) + { + if (int.TryParse(prop.Value.ToString(), out var count)) + { + depotItems[prop.Name] = count; + } + } } - - int count = -1; - bool hasValidCount = false; - - if (item["have"] != null) + catch (Exception ex) { - var haveValue = item["have"]?.ToString(); - hasValidCount = int.TryParse(haveValue, out count); + _logger.Warning(ex, "Failed to parse depot data format"); } + } - string countStr = hasValidCount ? count.FormatNumber(false) : "-1"; + // 如果新格式解析失败,尝试旧格式 + if (depotItems.Count == 0) + { + if (depotItems.Count == 0) + { + var arkplannerItems = details["arkplanner"]?["object"]?["items"] + ?.Cast() + ?? []; + + foreach (var item in arkplannerItems) + { + var id = (string?)item["id"]; + if (string.IsNullOrEmpty(id)) + { + continue; + } + + if (item["have"] != null && int.TryParse(item["have"]?.ToString(), out var count)) + { + depotItems[id] = count; + } + } + } + } + + // 转换为 DepotResult,按 ID 排序 + foreach (var kvp in depotItems.OrderBy(x => x.Key)) + { + var id = kvp.Key; + var count = kvp.Value; DepotResultDate result = new() { Id = id, Name = ItemListHelper.GetItemName(id), Image = ItemListHelper.GetItemImage(id), - Count = countStr, + Count = count, }; - if (hasValidCount && count > 0 && + if (count > 0 && count > AchievementTrackerHelper.Instance.GetProgress(AchievementIds.WarehouseMiser)) { AchievementTrackerHelper.Instance.SetProgress(AchievementIds.WarehouseMiser, count); @@ -521,8 +682,8 @@ public class ToolboxViewModel : Screen DepotResult.Add(result); } - ArkPlannerResult = details["arkplanner"]?["data"]?.ToString() ?? string.Empty; - LoliconResult = details["lolicon"]?["data"]?.ToString() ?? string.Empty; + // 标记缓存失效 + InvalidateDepotCache(); bool done = (bool)(details["done"] ?? false); if (!done) @@ -530,8 +691,25 @@ public class ToolboxViewModel : Screen return true; } + if (updateSyncTime) + { + // 从 Core 获取新数据,更新为当前 UTC 时间 + LastDepotSyncTime = DateTime.UtcNow; + } + else + { + // 从本地加载,读取保存的时间 + var syncTimeStr = details["syncTime"]?.ToString(Formatting.None)?.Trim('"'); + if (!string.IsNullOrEmpty(syncTimeStr) && + DateTime.TryParseExact(syncTimeStr, "O", null, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, + out var lastDepotSyncTime)) + { + LastDepotSyncTime = lastDepotSyncTime; + } + } + DepotInfo = LocalizationHelper.GetString("IdentificationCompleted"); - SaveDepotDetails(details); + SaveDepotDetails(); Instances.TaskQueueViewModel.UpdateDatePrompt(); return true; @@ -564,8 +742,120 @@ public class ToolboxViewModel : Screen private void DepotClear() { DepotResult.Clear(); - ArkPlannerResult = string.Empty; - LoliconResult = string.Empty; + InvalidateDepotCache(); + } + + // 需要排除的物品 ID(不统计到仓库) + private static readonly HashSet ExcludedItemIds = + [ + "3401", // 家具 + "3112", "3113", "3114", // 碳 + "4001", // 龙门币 + "4003", // 合成玉 + "4006", // 红票 + "5001", // 经验 + ]; + + /// + /// 检查物品 ID 是否应该被排除(不统计到仓库) + /// + /// 物品 ID + /// true 表示应该排除 + private static bool ShouldExcludeItem(string itemId) + { + // 排除特定 ID + if (ExcludedItemIds.Contains(itemId)) + { + return true; + } + + // 排除非纯数字的 ID + if (!int.TryParse(itemId, out _)) + { + return true; + } + + return false; + } + + /// + /// 根据 StageDrops 数据更新仓库 + /// + /// 关卡掉落数据列表 (ItemId, ItemName, Total, Add) + public void UpdateDepotFromDrops(List<(string ItemId, string ItemName, int Total, int Add)> drops) + { + if (drops == null || drops.Count == 0) + { + return; + } + + bool hasUpdates = false; + + foreach (var (itemId, _, total, add) in drops) + { + if (string.IsNullOrEmpty(itemId) || add <= 0) + { + continue; + } + + // 过滤不需要统计的物品 + if (ShouldExcludeItem(itemId)) + { + continue; + } + + // 查找现有仓库项 + var existingItem = DepotResult.FirstOrDefault(x => x.Id == itemId); + if (existingItem != null) + { + // 更新现有物品数量 + if (existingItem.Count >= 0) + { + var newCount = existingItem.Count + add; + existingItem.Count = newCount; + hasUpdates = true; + + // 更新成就进度 + if (newCount > AchievementTrackerHelper.Instance.GetProgress(AchievementIds.WarehouseMiser)) + { + AchievementTrackerHelper.Instance.SetProgress(AchievementIds.WarehouseMiser, newCount); + } + } + } + else + { + // 添加新物品 + var newItem = new DepotResultDate { + Id = itemId, + Name = ItemListHelper.GetItemName(itemId), + Image = ItemListHelper.GetItemImage(itemId), + Count = add, + }; + DepotResult.Add(newItem); + hasUpdates = true; + } + } + + // 如果有更新,重新排序并保存 + if (hasUpdates) + { + // 按 ID 排序(与 DepotParse 保持一致) + var sortedItems = DepotResult.OrderBy(x => x.Id).ToList(); + DepotResult.Clear(); + foreach (var item in sortedItems) + { + DepotResult.Add(item); + } + + // 标记缓存失效 + InvalidateDepotCache(); + + // 保存更新后的数据 + SaveDepotDetails(); + Instances.TaskQueueViewModel.UpdateDatePrompt(); + + _logger.Information("Depot updated from stage drops, {Count} items processed", drops.Count); + } } /// diff --git a/src/MaaWpfGui/Views/UI/TaskQueueView.xaml b/src/MaaWpfGui/Views/UI/TaskQueueView.xaml index 611c380a39..d590cb4e3b 100644 --- a/src/MaaWpfGui/Views/UI/TaskQueueView.xaml +++ b/src/MaaWpfGui/Views/UI/TaskQueueView.xaml @@ -387,8 +387,15 @@ + NormalOpacity="0.5"> + + + + + + + + diff --git a/src/MaaWpfGui/Views/UI/ToolboxView.xaml b/src/MaaWpfGui/Views/UI/ToolboxView.xaml index 12207549da..41ded61a4b 100644 --- a/src/MaaWpfGui/Views/UI/ToolboxView.xaml +++ b/src/MaaWpfGui/Views/UI/ToolboxView.xaml @@ -304,23 +304,34 @@ - + - + - + + + + + + + + + + TextWrapping="Wrap" + Visibility="{c:Binding 'DepotInfo.Length > 0'}" /> - +