mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-16 01:40:46 +08:00
Release v6.10.0-beta.3 (#16623)
## Summary by Sourcery 引入任务停滞检测与通知,增强成就界面交互,并接入相关设置和视图组件。 New Features: - 为运行中的任务添加可配置的停滞超时检测,当检测不到输出活动时,重复发送提醒通知。 - 允许在任务停滞时发送外部通知,由一个新的设置项进行控制。 - 支持从 growl 通知直接导航到特定成就,以及在成就列表中通过 ID 搜索并跳转到对应成就。 Enhancements: - 追踪并同步成就搜索文本,以便可以通过代码触发搜索,同时保持 UI 状态一致。 - 优化任务队列日志记录,使日志写入可以选择性地抑制用于停滞检测的活动通知。 - 添加一个 RootView 的 code-behind 类,用于初始化主 WPF 视图。 Chores: - 引入用于停滞超时和停滞通知设置的新配置键和值绑定,并更新相关的 XAML 和本地化资源。 <details> <summary>Original summary in English</summary> ## Summary by Sourcery Introduce task stall detection and notifications, enhance achievement UI interactions, and wire up supporting settings and view components. New Features: - Add configurable stall timeout detection for running tasks with repeated reminder notifications when no output activity is detected. - Allow external notifications to be sent when tasks are stalled, controlled by a new setting. - Enable navigating directly to a specific achievement from growl notifications and by ID-based searching in the achievement list. Enhancements: - Track and synchronize achievement search text so searches can be triggered programmatically while keeping the UI in sync. - Refine task queue logging so log writes can optionally suppress activity notifications used by stall detection. - Add a RootView code-behind class to initialize the main WPF view. Chores: - Introduce new configuration keys and bindings for stall timeout and stall notification settings, and update related XAML and localization resources. </details>
This commit is contained in:
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,3 +1,16 @@
|
||||
## v6.10.0-beta.3
|
||||
|
||||
### 新增 | New
|
||||
|
||||
* 点击成就横幅跳转至成就设置,自动打开成就列表并筛选对应成就 (#16537) @H2O-MERO @ABA2396
|
||||
* 任务日志输出停滞时发送通知, 替换 任务超时通知 (#16511) @H2O-MERO
|
||||
|
||||
### 其他 | Other
|
||||
|
||||
* 放宽对 RA-1 关卡名的检查 @ABA2396
|
||||
* 添加拆除设施的描述 @ABA2396
|
||||
* 删除辅助建设模式的描述 @ABA2396
|
||||
|
||||
## v6.10.0-beta.2
|
||||
|
||||
### 新增 | New
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"doc": "步骤 1(任务起点):OCR 识别 RA-1 并点击;右下角出现 <开启建设> 时进入步骤 2",
|
||||
"algorithm": "OcrDetect",
|
||||
"action": "ClickSelf",
|
||||
"text": ["RA-1"],
|
||||
"text": ["RA", "-1"],
|
||||
"withoutDet": true,
|
||||
"useRaw": false,
|
||||
"binThreshold": [200, 255],
|
||||
|
||||
Submodule src/MAAUnified updated: 1ee09448b3...ee18d23329
@@ -258,6 +258,7 @@ public static class ConfigurationKeys
|
||||
|
||||
public const string TaskTimeoutMinutes = "TimeOut.Timer.TaskTimeoutMinutes";
|
||||
public const string ReminderIntervalMinutes = "TimeOut.Timer.ReminderIntervalMinutes";
|
||||
public const string StallTimeoutMinutes = "TimeOut.Timer.StallTimeoutMinutes";
|
||||
|
||||
public const string BluestacksConfigPath = "Bluestacks.Config.Path";
|
||||
public const string BluestacksConfigKeyword = "Bluestacks.Config.Keyword";
|
||||
@@ -293,7 +294,7 @@ public static class ConfigurationKeys
|
||||
public const string ExternalNotificationSendWhenComplete = "ExternalNotification.SendWhenComplete";
|
||||
public const string ExternalNotificationEnableDetails = "ExternalNotification.EnableDetails";
|
||||
public const string ExternalNotificationSendWhenError = "ExternalNotification.SendWhenError";
|
||||
public const string ExternalNotificationSendWhenTimeout = "ExternalNotification.SendWhenTimeout";
|
||||
public const string ExternalNotificationSendWhenStalled = "ExternalNotification.SendWhenStalled";
|
||||
public const string ExternalNotificationSmtpServer = "ExternalNotification.Smtp.Server";
|
||||
public const string ExternalNotificationSmtpPort = "ExternalNotification.Smtp.Port";
|
||||
public const string ExternalNotificationSmtpUser = "ExternalNotification.Smtp.User";
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
@@ -103,9 +107,28 @@ public class AchievementTrackerHelper : PropertyChangedBase
|
||||
|
||||
public ICommand SearchCmd { get; }
|
||||
|
||||
private string _searchText = string.Empty;
|
||||
|
||||
public string SearchText
|
||||
{
|
||||
get => _searchText;
|
||||
set => SetAndNotify(ref _searchText, value ?? string.Empty);
|
||||
}
|
||||
|
||||
public void SearchAndSyncText(string text = "")
|
||||
{
|
||||
SearchText = text.Trim();
|
||||
Search(SearchText);
|
||||
}
|
||||
|
||||
public void Search(string text = "")
|
||||
{
|
||||
text = text.Trim();
|
||||
if (!string.Equals(SearchText, text, StringComparison.Ordinal))
|
||||
{
|
||||
SearchText = text;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
foreach (var achievement in Achievements)
|
||||
@@ -115,9 +138,14 @@ public class AchievementTrackerHelper : PropertyChangedBase
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var (_, value) in Achievements)
|
||||
foreach (var (key, value) in Achievements)
|
||||
{
|
||||
value.IsVisibleInSearch = value.Title.Contains(text) || value.Description.Contains(text) || value.Conditions.Contains(text) || value.ReleasePhaseTag.Contains(text);
|
||||
value.IsVisibleInSearch =
|
||||
key == text ||
|
||||
value.Title.Contains(text) ||
|
||||
value.Description.Contains(text) ||
|
||||
value.Conditions.Contains(text) ||
|
||||
value.ReleasePhaseTag.Contains(text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,6 +251,7 @@ public class AchievementTrackerHelper : PropertyChangedBase
|
||||
IconKey = "HangoverGeometry",
|
||||
IconBrushKey = achievement.MedalBrushKey,
|
||||
};
|
||||
_growlAchievementMap[growlInfo] = id;
|
||||
ShowInfo(growlInfo, forceStayOpen: forceStayOpen);
|
||||
}
|
||||
|
||||
@@ -276,6 +305,8 @@ public class AchievementTrackerHelper : PropertyChangedBase
|
||||
Save();
|
||||
}
|
||||
|
||||
private static readonly Dictionary<GrowlInfo, string> _growlAchievementMap = [];
|
||||
|
||||
private static readonly List<GrowlInfo> _pending = [];
|
||||
|
||||
public static void ShowInfo(GrowlInfo info, bool forceStayOpen = false)
|
||||
@@ -294,16 +325,132 @@ public class AchievementTrackerHelper : PropertyChangedBase
|
||||
return;
|
||||
}
|
||||
|
||||
var previousItems = Growl.GrowlPanel?.Children.OfType<UIElement>().ToHashSet() ?? [];
|
||||
Growl.Info(info);
|
||||
AttachGrowlClickHandler(info, previousItems);
|
||||
});
|
||||
}
|
||||
|
||||
private static void AttachGrowlClickHandler(GrowlInfo info, HashSet<UIElement> previousItems)
|
||||
{
|
||||
if (!_growlAchievementMap.TryGetValue(info, out var id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool TryAttachToNewItems()
|
||||
{
|
||||
if (Growl.GrowlPanel is not { } panel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool attached = false;
|
||||
foreach (var growlItem in panel.Children.OfType<UIElement>().Where(item => !previousItems.Contains(item)))
|
||||
{
|
||||
growlItem.RemoveHandler(UIElement.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(OnGrowlItemMouseDown));
|
||||
growlItem.AddHandler(
|
||||
UIElement.PreviewMouseLeftButtonDownEvent,
|
||||
new MouseButtonEventHandler(OnGrowlItemMouseDown),
|
||||
handledEventsToo: true);
|
||||
|
||||
if (growlItem is FrameworkElement element)
|
||||
{
|
||||
element.Tag = id;
|
||||
element.Unloaded -= OnGrowlItemUnloaded;
|
||||
element.Unloaded += OnGrowlItemUnloaded;
|
||||
}
|
||||
|
||||
attached = true;
|
||||
}
|
||||
|
||||
if (attached)
|
||||
{
|
||||
_growlAchievementMap.Remove(info);
|
||||
}
|
||||
|
||||
return attached;
|
||||
}
|
||||
|
||||
if (!TryAttachToNewItems())
|
||||
{
|
||||
Application.Current.Dispatcher.BeginInvoke(
|
||||
DispatcherPriority.Loaded,
|
||||
new Action(() => TryAttachToNewItems()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnGrowlItemUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is UIElement growlItem)
|
||||
{
|
||||
growlItem.RemoveHandler(UIElement.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(OnGrowlItemMouseDown));
|
||||
}
|
||||
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
element.Tag = null;
|
||||
element.Unloaded -= OnGrowlItemUnloaded;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnGrowlItemMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is not UIElement growlItem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var source = e.OriginalSource as DependencyObject;
|
||||
while (source != null && source != growlItem)
|
||||
{
|
||||
if (source is Button)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
source = VisualTreeHelper.GetParent(source);
|
||||
}
|
||||
|
||||
if (sender is FrameworkElement { Tag: string id } && !string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
NavigateToAchievement(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static void NavigateToAchievement(string id)
|
||||
{
|
||||
if (Instances.SettingsViewModel.Parent is IHaveActiveItem<Screen> conductor)
|
||||
{
|
||||
conductor.ActiveItem = Instances.SettingsViewModel;
|
||||
}
|
||||
|
||||
Application.Current.Dispatcher.BeginInvoke(
|
||||
DispatcherPriority.Loaded,
|
||||
new Action(() =>
|
||||
{
|
||||
var settings = Instances.SettingsViewModel.Settings;
|
||||
for (int i = 0; i < settings.Count; i++)
|
||||
{
|
||||
if (settings[i].Key == "AchievementSettings")
|
||||
{
|
||||
Instances.SettingsViewModel.SelectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AchievementSettingsUserControlModel.Instance.OnShowAchievementsClick(id);
|
||||
}));
|
||||
}
|
||||
|
||||
public static void TryShowPendingGrowls()
|
||||
{
|
||||
Execute.OnUIThread(() => {
|
||||
foreach (var info in _pending)
|
||||
{
|
||||
var previousItems = Growl.GrowlPanel?.Children.OfType<UIElement>().ToHashSet() ?? [];
|
||||
Growl.Info(info);
|
||||
AttachGrowlClickHandler(info, previousItems);
|
||||
}
|
||||
|
||||
_pending.Clear();
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<system:String x:Key="ExternalNotificationEnableDetails">Output Detailed Information</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenComplete">Send notification when complete</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenError">Send notification when task fails</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenTimeout">Send notification when task times out</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenStalled">Send notification when task log output stalls</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendSuccess">Send successfully</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendFail">Send failed</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendTest">Send Test</system:String>
|
||||
@@ -1463,7 +1463,7 @@ Cache files (such as maps, operator avatars, etc.) need to be regenerated after
|
||||
<!-- !Announcement -->
|
||||
<!-- Reclamation -->
|
||||
<system:String x:Key="ReclamationEarlyTipRelaunchAnchor" xml:space="preserve">
|
||||
Relaunch Anchor - Assisted Construction Mode:
|
||||
Relaunch Anchor:
|
||||
|
||||
Reward estimate: ~159 tokens + coordination points per run, ~3 min per round
|
||||
|
||||
@@ -1471,7 +1471,7 @@ Relaunch Anchor - Assisted Construction Mode:
|
||||
1. Manually enter RA-1, complete the tutorial, and return to the map
|
||||
2. After that, the task can be started when "Start Construction" appears at the bottom-right after clicking RA-1
|
||||
|
||||
Note: If the "Start with Extra Device" technology is unlocked, this task will not work properly
|
||||
Note: If you have unlocked technologies related to starting with extra items, please dismantle the facilities in the base that cause starting with extra items, such as Food Supply Stations, Drink Supply Stations, Large Animal Pens, etc.
|
||||
|
||||
Task flow:
|
||||
Automatically executes farming, construction, resource delivery, and settlement loop
|
||||
@@ -1525,7 +1525,8 @@ These locations are too high-level and can lead to file overwrites, config write
|
||||
<!-- TimeOutTimer -->
|
||||
<system:String x:Key="TaskTimeoutMinutes">Task timeout duration (minutes)</system:String>
|
||||
<system:String x:Key="ReminderIntervalMinutes">Reminder interval (minutes)</system:String>
|
||||
<system:String x:Key="TaskTimeoutWarning">The task has been running for more than {0} minutes (currently running for {1} minutes). If there are no further log updates for a long time, manual intervention may be required.</system:String>
|
||||
<system:String x:Key="StallTimeoutMinutes">Log output stall timeout (min)</system:String>
|
||||
<system:String x:Key="TaskStallWarning">Task log output has not been updated for {0} minutes (currently stalled for {1} minutes). If there is no further log output for a long time, manual intervention may be required.</system:String>
|
||||
<!-- !TimeOutTimer -->
|
||||
<!-- Achievement -->
|
||||
<system:String x:Key="AchievementCelebrate">🎉 Achievement Unlocked</system:String>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<system:String x:Key="ExternalNotificationEnableDetails">詳細情報を出力</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenComplete">タスク完了時に通知を送信</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenError">タスクが失敗したときに通知を送信</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenTimeout">タスクがタイムアウトしたときに通知を送信する</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenStalled">タスクログ出力停滞時に通知を送信</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendSuccess">正常に送信</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendFail">送信に失敗しました</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendTest">テストを送信する</system:String>
|
||||
@@ -1464,7 +1464,7 @@ DEBUGディレクトリに保存される画像には数量制限があり、超
|
||||
<!-- !Announcement -->
|
||||
<!-- Reclamation -->
|
||||
<system:String x:Key="ReclamationEarlyTipRelaunchAnchor" xml:space="preserve">
|
||||
再起の錨 - 支援建設モード:
|
||||
再起の錨:
|
||||
|
||||
報酬目安:1回あたり約159トークン+統括ポイント、所要時間約3分
|
||||
|
||||
@@ -1472,7 +1472,7 @@ DEBUGディレクトリに保存される画像には数量制限があり、超
|
||||
1. 手動でRA-1に入り、チュートリアルを完了してマップに戻る
|
||||
2. 以降、RA-1をクリックした後に右下に「建設開始」が表示されたらタスクを開始可能
|
||||
|
||||
注意:「初期追加携帯装置」のテクノロジーをアンロックしていると正常に動作しません
|
||||
注意:「初期追加携帯アイテム」に関連するテクノロジーをアンロックしている場合、基地内で初期追加携帯アイテムの原因となる施設(食品供給所、飲料供給所、大型獣舎など)を解体してください
|
||||
|
||||
タスクの流れ:
|
||||
精耕細作、建設、資源納入、決済を自動実行
|
||||
@@ -1526,7 +1526,8 @@ MAA を複数開くには、新しい MAA を他のフォルダにコピーし
|
||||
<!-- TimeOutTimer -->
|
||||
<system:String x:Key="TaskTimeoutMinutes">タスクのタイムアウト時間(分)</system:String>
|
||||
<system:String x:Key="ReminderIntervalMinutes">リマインダー間隔時間(分)</system:String>
|
||||
<system:String x:Key="TaskTimeoutWarning">タスクが {0} 分以上実行されています(現在 {1} 分実行中)。長時間ログの更新がない場合、手動で介入する必要があるかもしれません。</system:String>
|
||||
<system:String x:Key="StallTimeoutMinutes">ログ出力停滞タイムアウト(分)</system:String>
|
||||
<system:String x:Key="TaskStallWarning">タスクログ出力が {0} 分間更新されていません(現在 {1} 分間停滞)。長時間ログ出力がない場合は、手動での操作が必要になる可能性があります。</system:String>
|
||||
<!-- !TimeOutTimer -->
|
||||
<!-- Achievement -->
|
||||
<system:String x:Key="AchievementCelebrate">🎉 実績を獲得した</system:String>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<system:String x:Key="ExternalNotificationEnableDetails">상세 정보 출력</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenComplete">작업 완료 시 알림 전송</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenError">작업 실패 시 알림 전송</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenTimeout">작업 시간 초과 시 알림 전송</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenStalled">작업 로그 출력 정지 시 알림 전송</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendSuccess">전송 성공</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendFail">전송 실패</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendTest">테스트</system:String>
|
||||
@@ -1465,7 +1465,7 @@ DEBUG 디렉토리에 저장된 이미지는 수량 제한이 있으며 초과
|
||||
<!-- !Announcement -->
|
||||
<!-- Reclamation -->
|
||||
<system:String x:Key="ReclamationEarlyTipRelaunchAnchor" xml:space="preserve">
|
||||
재시작 앵커 - 지원 건설 모드:
|
||||
재시작 앵커:
|
||||
|
||||
수익 참고: 회당 약 159토큰 + 통괄 포인트, 소요 시간 약 3분
|
||||
|
||||
@@ -1473,7 +1473,7 @@ DEBUG 디렉토리에 저장된 이미지는 수량 제한이 있으며 초과
|
||||
1. 수동으로 RA-1에 진입하여 튜토리얼을 완료하고 맵으로 돌아옵니다
|
||||
2. 이후 RA-1 클릭 후 오른쪽 아래에 "건설 시작"이 나타나면 작업을 시작할 수 있습니다
|
||||
|
||||
참고: "초기 추가 장비 휴대" 기술을 해제한 경우 정상 작동하지 않습니다
|
||||
참고: 초기 추가 아이템 휴대 관련 기술을 해제한 경우, 기지 내에서 초기 추가 아이템을 휴대하게 하는 시설(식품 공급소, 음료 공급소, 대형 축사 등)을 철거하세요
|
||||
|
||||
작업 흐름:
|
||||
정경세작, 건설, 자원 납품, 결제를 자동 실행</system:String>
|
||||
@@ -1526,7 +1526,8 @@ MAA를 독립된 새 폴더에 압축 해제하거나, MAA에 속하지 않는 D
|
||||
<!-- TimeOutTimer -->
|
||||
<system:String x:Key="TaskTimeoutMinutes">작업 타임아웃 시간 (분)</system:String>
|
||||
<system:String x:Key="ReminderIntervalMinutes">알림 간격 (분)</system:String>
|
||||
<system:String x:Key="TaskTimeoutWarning">작업이 {0}분을 초과하여 {1}분째 실행 중입니다\n장시간 로그의 갱신이 없다면 수동 조작이 필요할 수 있습니다</system:String>
|
||||
<system:String x:Key="StallTimeoutMinutes">로그 출력 정지 타임아웃 (분)</system:String>
|
||||
<system:String x:Key="TaskStallWarning">작업 로그 출력이 {0}분 동안 업데이트되지 않았습니다(현재 {1}분 정지). 오랫동안 로그 출력이 없으면 수동 개입이 필요할 수 있습니다.</system:String>
|
||||
<!-- !TimeOutTimer -->
|
||||
<!-- Achievement -->
|
||||
<system:String x:Key="AchievementCelebrate">🎉 도전과제 달성</system:String>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<system:String x:Key="ExternalNotificationEnableDetails">输出详细信息</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenComplete">任务完成后发送通知</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenError">任务出错时发送通知</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenTimeout">任务超时时发送通知</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenStalled">任务日志输出停滞时发送通知</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendSuccess">发送成功</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendFail">发送失败</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendTest">发送测试</system:String>
|
||||
@@ -1464,7 +1464,7 @@ DEBUG 目录下保存的图片有数量限制,超出后会自动清理旧图
|
||||
<!-- !Announcement -->
|
||||
<!-- Reclamation -->
|
||||
<system:String x:Key="ReclamationEarlyTipRelaunchAnchor" xml:space="preserve">
|
||||
重启锚点 - 辅助建设模式:
|
||||
重启锚点:
|
||||
|
||||
收益参考:每把约 159 代币 + 统筹点数,单轮耗时约 3 分钟
|
||||
|
||||
@@ -1472,7 +1472,7 @@ DEBUG 目录下保存的图片有数量限制,超出后会自动清理旧图
|
||||
1. 手动进入 RA-1,完成新手教程,直到回到大地图
|
||||
2. 之后每次任务均可在 RA-1 右下角出现“开启建设”时启动
|
||||
|
||||
注意:如果已解锁开局额外携带装置的科技,将无法正常使用
|
||||
注意:如果已解锁开局额外携带物品的相关科技,请将基地内会导致开局额外携带物品的设施拆除,如食品供给站、饮品供给站、大型兽栏等
|
||||
|
||||
任务流程:
|
||||
自动执行精耕细作、建设、交付资源、结算循环
|
||||
@@ -1525,8 +1525,9 @@ DEBUG 目录下保存的图片有数量限制,超出后会自动清理旧图
|
||||
<!-- !SimpleEncryptionHelper -->
|
||||
<!-- TimeOutTimer -->
|
||||
<system:String x:Key="TaskTimeoutMinutes">任务超时时间(分钟)</system:String>
|
||||
<system:String x:Key="ReminderIntervalMinutes">提醒间隔时间(分钟)</system:String>
|
||||
<system:String x:Key="TaskTimeoutWarning">任务已运行超过 {0} 分钟(当前已运行 {1} 分钟)。如果长时间无进一步日志更新,可能需要手动干预。</system:String>
|
||||
<system:String x:Key="ReminderIntervalMinutes">通知间隔时间(分钟)</system:String>
|
||||
<system:String x:Key="StallTimeoutMinutes">日志输出停滞超时时间(分钟)</system:String>
|
||||
<system:String x:Key="TaskStallWarning">任务日志输出已超过 {0} 分钟无更新(当前已停滞 {1} 分钟)。如果长时间无进一步日志输出,可能需要手动干预。</system:String>
|
||||
<!-- !TimeOutTimer -->
|
||||
<!-- Achievement -->
|
||||
<system:String x:Key="AchievementCelebrate">🎉 达成成就</system:String>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<system:String x:Key="ExternalNotificationEnableDetails">輸出詳細資訊</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenComplete">任務完成後發送通知</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenError">任務出錯時發送通知</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenTimeout">任務逾時時發送通知</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendWhenStalled">任務日誌輸出停滯時發送通知</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendSuccess">發送成功</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendFail">發送失敗</system:String>
|
||||
<system:String x:Key="ExternalNotificationSendTest">發送測試</system:String>
|
||||
@@ -1464,7 +1464,7 @@ DEBUG 目錄下儲存的圖片有數量限制,超出後會自動清理舊圖
|
||||
<!-- !Announcement -->
|
||||
<!-- Reclamation -->
|
||||
<system:String x:Key="ReclamationEarlyTipRelaunchAnchor" xml:space="preserve">
|
||||
重啟錨點 - 輔助建設模式:
|
||||
重啟錨點:
|
||||
|
||||
收益參考:每把約 159 代幣 + 統籌點數,單輪耗時約 3 分
|
||||
|
||||
@@ -1472,7 +1472,7 @@ DEBUG 目錄下儲存的圖片有數量限制,超出後會自動清理舊圖
|
||||
1. 手動進入 RA-1,完成新手教學,直到回到大地圖
|
||||
2. 之後每次任務均可在 RA-1 右下角出現「開啟建設」時啟動
|
||||
|
||||
注意:如果已解鎖開局額外攜帶裝置的科技,將無法正常使用
|
||||
注意:如果已解鎖開局額外攜帶物品的相關科技,請將基地內會導致開局額外攜帶物品的設施拆除,如食品供給站、飲品供給站、大型獸欄等
|
||||
|
||||
任務流程:
|
||||
自動執行精耕細作、建設、交付資源、結算循環
|
||||
@@ -1525,8 +1525,9 @@ DEBUG 目錄下儲存的圖片有數量限制,超出後會自動清理舊圖
|
||||
<!-- !SimpleEncryptionHelper -->
|
||||
<!-- TimeOutTimer -->
|
||||
<system:String x:Key="TaskTimeoutMinutes">任務逾時時間(分鐘)</system:String>
|
||||
<system:String x:Key="ReminderIntervalMinutes">提醒間隔時間(分鐘)</system:String>
|
||||
<system:String x:Key="TaskTimeoutWarning">任務已執行超過 {0} 分鐘(目前已執行 {1} 分鐘)。若長時間無進一步日誌更新,可能需要手動干預。</system:String>
|
||||
<system:String x:Key="ReminderIntervalMinutes">通知間隔時間(分鐘)</system:String>
|
||||
<system:String x:Key="StallTimeoutMinutes">日誌輸出停滯超時時間(分鐘)</system:String>
|
||||
<system:String x:Key="TaskStallWarning">任務日誌輸出已超過 {0} 分鐘無更新(當前已停滯 {1} 分鐘)。如果長時間無進一步日誌輸出,可能需要手動干預。</system:String>
|
||||
<!-- !TimeOutTimer -->
|
||||
<!-- Achievement -->
|
||||
<system:String x:Key="AchievementCelebrate">🎉 達成成就</system:String>
|
||||
|
||||
@@ -46,6 +46,7 @@ public class RunningState
|
||||
|
||||
_timeoutReminderTimer.Interval = ReminderIntervalMinutes * 60 * 1000;
|
||||
_timeoutReminderTimer.Elapsed += TimeoutReminderTimer_Elapsed;
|
||||
_stallTimer.Elapsed += StallTimer_Elapsed;
|
||||
}
|
||||
|
||||
public static RunningState Instance
|
||||
@@ -58,6 +59,9 @@ public class RunningState
|
||||
|
||||
// 超时相关字段
|
||||
private readonly System.Timers.Timer _timeoutReminderTimer = new();
|
||||
private readonly System.Timers.Timer _stallTimer = new();
|
||||
private int _stallAccumulatedCount = 0;
|
||||
private bool _stallIsFirstFire = true;
|
||||
private DateTime? _taskStartTime;
|
||||
|
||||
public int TaskTimeoutMinutes { get; set; } = SettingsViewModel.GameSettings.TaskTimeoutMinutes;
|
||||
@@ -79,18 +83,61 @@ public class RunningState
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int _stallTimeoutMinutes = SettingsViewModel.GameSettings.StallTimeoutMinutes;
|
||||
|
||||
public int StallTimeoutMinutes
|
||||
{
|
||||
get => _stallTimeoutMinutes;
|
||||
set {
|
||||
_stallTimeoutMinutes = value;
|
||||
_stallIsFirstFire = true;
|
||||
if (_stallTimer.Enabled)
|
||||
{
|
||||
_stallTimer.Stop();
|
||||
if (value > 0)
|
||||
{
|
||||
_stallTimer.Interval = value * 60 * 1000;
|
||||
_stallTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<string>? StallOccurred;
|
||||
|
||||
public void NotifyOutputActivity()
|
||||
{
|
||||
_stallAccumulatedCount = 0;
|
||||
_stallIsFirstFire = true;
|
||||
if (_stallTimer.Enabled && StallTimeoutMinutes > 0)
|
||||
{
|
||||
_stallTimer.Interval = StallTimeoutMinutes * 60 * 1000;
|
||||
_stallTimer.Stop();
|
||||
_stallTimer.Start();
|
||||
}
|
||||
}
|
||||
// 超时事件
|
||||
public event EventHandler<string>? TimeoutOccurred;
|
||||
|
||||
public void StartTimeoutTimer()
|
||||
{
|
||||
_taskStartTime = DateTime.Now;
|
||||
_timeoutReminderTimer.Start();
|
||||
_stallAccumulatedCount = 0;
|
||||
_stallIsFirstFire = true;
|
||||
if (StallTimeoutMinutes > 0)
|
||||
{
|
||||
_stallTimer.Interval = StallTimeoutMinutes * 60 * 1000;
|
||||
_stallTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void StopTimeoutTimer()
|
||||
{
|
||||
_timeoutReminderTimer.Stop();
|
||||
_stallTimer.Stop();
|
||||
_stallAccumulatedCount = 0;
|
||||
_stallIsFirstFire = true;
|
||||
_taskStartTime = null;
|
||||
}
|
||||
|
||||
@@ -120,15 +167,29 @@ public class RunningState
|
||||
return;
|
||||
}
|
||||
|
||||
// 每隔 ReminderIntervalMinutes 提示一次
|
||||
var message = string.Format(
|
||||
LocalizationHelper.GetString("TaskTimeoutWarning"),
|
||||
TaskTimeoutMinutes,
|
||||
Math.Round(elapsedMinutes));
|
||||
|
||||
AchievementTrackerHelper.Instance.Unlock(AchievementIds.LongTaskTimeout);
|
||||
}
|
||||
|
||||
TimeoutOccurred?.Invoke(this, message);
|
||||
private void StallTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
_stallTimer.Stop();
|
||||
_stallAccumulatedCount++;
|
||||
var accumulatedMinutes = StallTimeoutMinutes + ((_stallAccumulatedCount - 1) * ReminderIntervalMinutes);
|
||||
var message = string.Format(
|
||||
LocalizationHelper.GetString("TaskStallWarning"),
|
||||
StallTimeoutMinutes,
|
||||
accumulatedMinutes);
|
||||
StallOccurred?.Invoke(this, message);
|
||||
if (StallTimeoutMinutes > 0)
|
||||
{
|
||||
if (_stallIsFirstFire)
|
||||
{
|
||||
_stallTimer.Interval = ReminderIntervalMinutes * 60 * 1000;
|
||||
_stallIsFirstFire = false;
|
||||
}
|
||||
|
||||
_stallTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _idle = true;
|
||||
|
||||
@@ -610,7 +610,7 @@ public class TaskQueueViewModel : Screen
|
||||
Instances.Data.ClearCache();
|
||||
}
|
||||
};
|
||||
_runningState.TimeoutOccurred += RunningState_TimeOut;
|
||||
_runningState.StallOccurred += RunningState_Stalled;
|
||||
|
||||
if (Instances.VersionUpdateDialogViewModel.IsDebugVersion() || File.Exists("DEBUG") || File.Exists("DEBUG.txt"))
|
||||
{
|
||||
@@ -619,16 +619,17 @@ public class TaskQueueViewModel : Screen
|
||||
}
|
||||
}
|
||||
|
||||
private void RunningState_TimeOut(object? sender, string message)
|
||||
{
|
||||
Execute.OnUIThread(() => {
|
||||
AddLog(message, UiLogColor.Warning);
|
||||
ToastNotification.ShowDirect(message);
|
||||
if (!SettingsViewModel.ExternalNotificationSettings.ExternalNotificationSendWhenTimeout)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void RunningState_Stalled(object? sender, string message)
|
||||
{
|
||||
if (!SettingsViewModel.ExternalNotificationSettings.ExternalNotificationSendWhenStalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Execute.OnUIThread(() => {
|
||||
AddLog(message, UiLogColor.Warning, notifyActivity: false);
|
||||
ToastNotification.ShowDirect(message);
|
||||
var lastLogs = LogItemViewModels
|
||||
.TakeLast(5)
|
||||
.Aggregate(string.Empty, (current, logItem) => current + $"[{logItem.Time}][{logItem.Color}]{logItem.Content}\n");
|
||||
@@ -1185,8 +1186,14 @@ public class TaskQueueViewModel : Screen
|
||||
bool updateCardImage = false,
|
||||
bool fetchLatestImage = false,
|
||||
bool useCardImageAsToolTip = false,
|
||||
LogCardSplitMode splitMode = LogCardSplitMode.None)
|
||||
LogCardSplitMode splitMode = LogCardSplitMode.None,
|
||||
bool notifyActivity = true)
|
||||
{
|
||||
if (notifyActivity)
|
||||
{
|
||||
RunningState.Instance.NotifyOutputActivity();
|
||||
}
|
||||
|
||||
bool isEmpty = string.IsNullOrEmpty(content);
|
||||
bool needsBeforeSplit = splitMode == LogCardSplitMode.Before || splitMode == LogCardSplitMode.Both;
|
||||
bool needsAfterSplit = splitMode == LogCardSplitMode.After || splitMode == LogCardSplitMode.Both;
|
||||
|
||||
@@ -108,9 +108,9 @@ public class AchievementSettingsUserControlModel : PropertyChangedBase
|
||||
}
|
||||
}
|
||||
|
||||
private static Window? _achievementsWindow;
|
||||
private static AchievementListDialogView? _achievementsWindow;
|
||||
|
||||
public void OnShowAchievementsClick()
|
||||
public void OnShowAchievementsClick(string? achievementId = null)
|
||||
{
|
||||
AchievementTrackerHelper.Instance.Unlock(AchievementIds.AchievementObserver);
|
||||
if (_achievementsWindow is null)
|
||||
@@ -125,6 +125,11 @@ public class AchievementSettingsUserControlModel : PropertyChangedBase
|
||||
}
|
||||
|
||||
WindowManager.ShowWindow(_achievementsWindow);
|
||||
|
||||
if (achievementId != null)
|
||||
{
|
||||
AchievementTrackerHelper.Instance.SearchAndSyncText(achievementId);
|
||||
}
|
||||
}
|
||||
|
||||
private int _clickCount = 0;
|
||||
|
||||
@@ -78,14 +78,15 @@ public class ExternalNotificationSettingsUserControlModel : PropertyChangedBase
|
||||
}
|
||||
}
|
||||
|
||||
private bool _externalNotificationSendWhenTimeout = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.ExternalNotificationSendWhenTimeout, bool.TrueString));
|
||||
|
||||
public bool ExternalNotificationSendWhenTimeout
|
||||
private bool _externalNotificationSendWhenStalled = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.ExternalNotificationSendWhenStalled, bool.FalseString));
|
||||
|
||||
public bool ExternalNotificationSendWhenStalled
|
||||
{
|
||||
get => _externalNotificationSendWhenTimeout;
|
||||
get => _externalNotificationSendWhenStalled;
|
||||
set {
|
||||
SetAndNotify(ref _externalNotificationSendWhenTimeout, value);
|
||||
ConfigurationHelper.SetValue(ConfigurationKeys.ExternalNotificationSendWhenTimeout, value.ToString());
|
||||
SetAndNotify(ref _externalNotificationSendWhenStalled, value);
|
||||
ConfigurationHelper.SetValue(ConfigurationKeys.ExternalNotificationSendWhenStalled, value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -370,5 +370,17 @@ public class GameSettingsUserControlModel : PropertyChangedBase
|
||||
}
|
||||
}
|
||||
|
||||
private int _stallTimeoutMinutes = ConfigurationHelper.GetValue(ConfigurationKeys.StallTimeoutMinutes, 25);
|
||||
|
||||
public int StallTimeoutMinutes
|
||||
{
|
||||
get => _stallTimeoutMinutes;
|
||||
set {
|
||||
SetAndNotify(ref _stallTimeoutMinutes, value);
|
||||
_runningState.StallTimeoutMinutes = value;
|
||||
ConfigurationHelper.SetValue(ConfigurationKeys.StallTimeoutMinutes, value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
#endregion 任务超时
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
Background="{DynamicResource MdXamlBackground}">
|
||||
<Grid>
|
||||
<ListBox
|
||||
x:Name="AchievementListBox"
|
||||
Margin="0,0,10,0"
|
||||
Background="{DynamicResource MdXamlBackground}"
|
||||
BorderThickness="0"
|
||||
@@ -129,7 +130,8 @@
|
||||
VerticalAlignment="Top"
|
||||
Background="{DynamicResource MouseOverRegionBrushOpacity75}"
|
||||
Command="{Binding SearchCmd, Source={x:Static helper:AchievementTrackerHelper.Instance}}"
|
||||
CommandParameter="{Binding Text, RelativeSource={RelativeSource Self}}"
|
||||
CommandParameter="{Binding SearchText, Source={x:Static helper:AchievementTrackerHelper.Instance}}"
|
||||
Text="{Binding SearchText, Source={x:Static helper:AchievementTrackerHelper.Instance}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsRealTime="True" />
|
||||
</Grid>
|
||||
</hc:Window>
|
||||
|
||||
@@ -33,4 +33,5 @@ public partial class AchievementListDialogView
|
||||
// 关闭窗口时执行一次空搜索,重置可见性
|
||||
AchievementTrackerHelper.Instance.Search();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
d:Height="200"
|
||||
d:Width="100"
|
||||
VerticalScrollBarVisibility="Hidden">
|
||||
<StackPanel Margin="0,0,0,5" hc:Growl.GrowlParent="True" />
|
||||
<StackPanel x:Name="AchievementGrowlPanel" Margin="0,0,0,5" hc:Growl.GrowlParent="True" />
|
||||
</ScrollViewer>
|
||||
<hc:GifImage
|
||||
Width="100"
|
||||
|
||||
22
src/MaaWpfGui/Views/UI/RootView.xaml.cs
Normal file
22
src/MaaWpfGui/Views/UI/RootView.xaml.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
// <copyright file="RootView.xaml.cs" company="MaaAssistantArknights">
|
||||
// Part of the MaaWpfGui project, maintained by the MaaAssistantArknights team (Maa Team)
|
||||
// Copyright (C) 2021-2025 MaaAssistantArknights Contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License v3.0 only as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY
|
||||
// </copyright>
|
||||
|
||||
namespace MaaWpfGui.Views.UI;
|
||||
|
||||
public partial class RootView
|
||||
{
|
||||
public RootView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -91,12 +91,12 @@
|
||||
VerticalAlignment="Top"
|
||||
Content="{DynamicResource ExternalNotificationSendWhenError}"
|
||||
IsChecked="{Binding ExternalNotificationSendWhenError}" />
|
||||
<CheckBox
|
||||
Margin="5"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Content="{DynamicResource ExternalNotificationSendWhenTimeout}"
|
||||
IsChecked="{Binding ExternalNotificationSendWhenTimeout}" />
|
||||
<CheckBox
|
||||
Margin="5"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Content="{DynamicResource ExternalNotificationSendWhenStalled}"
|
||||
IsChecked="{Binding ExternalNotificationSendWhenStalled}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@@ -188,11 +188,11 @@
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<hc:NumericUpDown
|
||||
Margin="5"
|
||||
hc:InfoElement.Title="{DynamicResource TaskTimeoutMinutes}"
|
||||
hc:InfoElement.Title="{DynamicResource StallTimeoutMinutes}"
|
||||
hc:TitleElement.TitlePlacement="Left"
|
||||
InputMethod.IsInputMethodEnabled="False"
|
||||
Minimum="0"
|
||||
Value="{Binding TaskTimeoutMinutes}" />
|
||||
Minimum="1"
|
||||
Value="{Binding StallTimeoutMinutes}" />
|
||||
<hc:NumericUpDown
|
||||
Margin="5"
|
||||
hc:InfoElement.Title="{DynamicResource ReminderIntervalMinutes}"
|
||||
|
||||
Reference in New Issue
Block a user