mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-15 17:30:27 +08:00
feat: 理智作战支持设定目标材料最大库存 (#16487)
* feat: 理智作战支持设定目标材料最大库存 * chore: 调整翻译绑定,修改运行时 IsEnable,移除无用属性 * chore: 调整描述 * chore: 调整缩进 * perf: 移除不必要的变更判断 * feat: 任务开始时刷新库存 * rft: rename * chore: 运行时不刷新仓库数据,仅在进入任务时刷新一次 --------- Co-authored-by: status102 <102887808+status102@users.noreply.github.com>
This commit is contained in:
@@ -64,6 +64,11 @@ public class FightTask : BaseTask, IJsonOnDeserialized
|
||||
/// </summary>
|
||||
public int DropCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 指定材料按库存目标计算
|
||||
/// </summary>
|
||||
public bool IsInventoryTarget { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets 是否启用次数限制
|
||||
/// </summary>
|
||||
|
||||
@@ -2873,9 +2873,9 @@ public class AsstProxy
|
||||
|
||||
public IReadOnlyDictionary<AsstTaskId, (TaskType Type, TaskStatus Status)> TasksStatus => new Dictionary<AsstTaskId, (TaskType, TaskStatus)>(_tasksStatus);
|
||||
|
||||
public delegate void TaskItemStatusDelegate(int taskId, TaskItemStatus status);
|
||||
public delegate void TaskStatusDelegate(int taskId, TaskItemStatus status);
|
||||
|
||||
public event TaskItemStatusDelegate? OnTaskItemStatusChanged;
|
||||
public event TaskStatusDelegate? OnTaskStatusChanged;
|
||||
|
||||
private bool UpdateTaskStatus(AsstTaskId id, TaskStatus status)
|
||||
{
|
||||
@@ -2896,7 +2896,7 @@ public class AsstProxy
|
||||
}
|
||||
|
||||
_tasksStatus[id] = (value.Type, status);
|
||||
OnTaskItemStatusChanged?.Invoke(id, (TaskItemStatus)status);
|
||||
OnTaskStatusChanged?.Invoke(id, (TaskItemStatus)status);
|
||||
if (status == TaskStatus.InProgress)
|
||||
{
|
||||
TaskSettingVisibilityInfo.Instance.NotifyOfTaskStatus();
|
||||
|
||||
@@ -322,7 +322,11 @@ To customize rotation schedules, please use 「{key=InfrastModeCustom}」.</syst
|
||||
<system:String x:Key="CheckBoxesNotSaved">This option is effective once</system:String>
|
||||
<system:String x:Key="CheckBoxesNotSavedAsNull">This option only takes effect once when right-clicked</system:String>
|
||||
<system:String x:Key="CheckBoxesNotSavedAsNullInvert">This option only skips once when right-clicked</system:String>
|
||||
<system:String x:Key="SpecifiedDropsTip">This feature would not automatically calculate the optimal stage</system:String>
|
||||
<system:String x:Key="SpecifiedDropsTip" xml:space="preserve">The stage entered is still determined by the options below: 「{key=StageSelect}」 / 「{key=StageSelect2}」.
|
||||
• {key=Quantity}: uses the selected material's drop count in this combat task as the target, and stops when the set amount is reached.
|
||||
• {key=TargetInventory}: uses the saved inventory data from 「{key=Toolbox}-{key=DepotRecognition}」 and only farms until the target stock is reached.
|
||||
• Inventory data is cached and may differ from your real stock after manual farming, crafting, or material use. Sync it with 「{key=UserDataUpdate}」 or 「{key=Toolbox}-{key=DepotRecognition}」.
|
||||
• When {key=TargetInventory} is enabled, the material and amount cannot be changed during a run.</system:String>
|
||||
<system:String x:Key="StartupUpdateCheck">Startup Update Check</system:String>
|
||||
<system:String x:Key="UpdateAutoCheck">Scheduled Update Check</system:String>
|
||||
<system:String x:Key="UpdateAutoCheckTip">In-game 00:00 / 18:00 (Server local time 04:00 / 22:00)</system:String>
|
||||
@@ -775,7 +779,9 @@ Right-clicking other options will check/uncheck this option simultaneously</syst
|
||||
<system:String x:Key="UseOriginitePrime">Use Originium</system:String>
|
||||
<system:String x:Key="PerformBattles">Perform Battles</system:String>
|
||||
<system:String x:Key="AssignedMaterial">Material</system:String>
|
||||
<system:String x:Key="Quantity">Quantity</system:String>
|
||||
<system:String x:Key="Quantity">Farm</system:String>
|
||||
<system:String x:Key="KeepInventory">Keep Inventory</system:String>
|
||||
<system:String x:Key="TargetInventory">Goal</system:String>
|
||||
<system:String x:Key="Series">Series</system:String>
|
||||
<system:String x:Key="SeriesTip" xml:space="preserve">• AUTO:
|
||||
Automatically identify the maximum proxy multiplier of the level, maintain the maximum proxy multiplier and do not overflow sanity after using sanity potion
|
||||
@@ -805,6 +811,7 @@ Do not adjust the proxy multiplier setting in the game</system:String>
|
||||
<system:String x:Key="DaysLeftOpen" xml:space="preserve">Days left open: </system:String>
|
||||
<system:String x:Key="Inventory">Inventory</system:String>
|
||||
<system:String x:Key="InventoryUpdateTip">Inventory data can be updated via 「{key=Toolbox}-{key=DepotRecognition}」</system:String>
|
||||
<system:String x:Key="SpecifiedDropsInventoryUnavailable">Task "{0}" uses {key=TargetInventory}, but no inventory data is currently available, so it was skipped. Update it via 「{key=Toolbox}-{key=DepotRecognition}」.</system:String>
|
||||
<system:String x:Key="LessThanOneDay">Less than 1 day</system:String>
|
||||
<system:String x:Key="1-7">1-7</system:String>
|
||||
<system:String x:Key="CE-6">CE-6</system:String>
|
||||
|
||||
@@ -322,7 +322,11 @@
|
||||
<system:String x:Key="CheckBoxesNotSaved">このオプションは一度有効です</system:String>
|
||||
<system:String x:Key="CheckBoxesNotSavedAsNull">このオプションは、右クリックで選択した時のみ一度だけ有効です</system:String>
|
||||
<system:String x:Key="CheckBoxesNotSavedAsNullInvert">このオプションは、右クリックで選択した時のみ一度だけスキップします</system:String>
|
||||
<system:String x:Key="SpecifiedDropsTip">この機能では最適なステージを自動的に計算しません</system:String>
|
||||
<system:String x:Key="SpecifiedDropsTip" xml:space="preserve">実際に入るステージは、引き続き下の 「{key=StageSelect}」 / 「{key=StageSelect2}」 で決まります。
|
||||
• {key=Quantity}: この理性作戦タスク中で、その素材のドロップ数が設定値に達するまで周回します。
|
||||
• {key=TargetInventory}: 「{key=Toolbox}-{key=DepotRecognition}」 に保存された在庫データを参照し、設定した在庫数になるまで補充します。
|
||||
• 在庫データはキャッシュのため、手動周回や素材の使用後は実際とずれる場合があります。「{key=UserDataUpdate}」 または 「{key=Toolbox}-{key=DepotRecognition}」 で同期してください。
|
||||
• {key=TargetInventory} を有効にすると、実行中は素材や数量を変更できません。</system:String>
|
||||
<system:String x:Key="StartupUpdateCheck">起動時の更新チェック</system:String>
|
||||
<system:String x:Key="UpdateAutoCheck">定期的な更新チェック</system:String>
|
||||
<system:String x:Key="UpdateAutoCheckTip">ゲーム内 00:00 / 18:00(サーバー現地時間 04:00 / 22:00)</system:String>
|
||||
@@ -777,6 +781,8 @@ C:\\leidian\\LDPlayer9
|
||||
<system:String x:Key="PerformBattles">周回数指定</system:String>
|
||||
<system:String x:Key="AssignedMaterial">素材の指定</system:String>
|
||||
<system:String x:Key="Quantity">ドロップ数</system:String>
|
||||
<system:String x:Key="KeepInventory">在庫を維持</system:String>
|
||||
<system:String x:Key="TargetInventory">目標在庫</system:String>
|
||||
<system:String x:Key="Series">連戦回数</system:String>
|
||||
<system:String x:Key="SeriesTip" xml:space="preserve">• 自動:
|
||||
レベルの最大代理乗数を自動で判別し、最大代理乗数を維持し、正気ポーション使用後に正気度オーバーフローが発生しないようにします。
|
||||
@@ -806,6 +812,7 @@ C:\\leidian\\LDPlayer9
|
||||
<system:String x:Key="DaysLeftOpen" xml:space="preserve">公開日まで: </system:String>
|
||||
<system:String x:Key="Inventory">在庫</system:String>
|
||||
<system:String x:Key="InventoryUpdateTip">在庫データは 「{key=Toolbox}-{key=DepotRecognition}」 で更新できます</system:String>
|
||||
<system:String x:Key="SpecifiedDropsInventoryUnavailable">タスク「{0}」では {key=TargetInventory} が有効ですが、利用可能な在庫データがないためスキップされました。「{key=Toolbox}-{key=DepotRecognition}」 で在庫を更新してください。</system:String>
|
||||
<system:String x:Key="LessThanOneDay">1日以内</system:String>
|
||||
<system:String x:Key="1-7">1-7</system:String>
|
||||
<system:String x:Key="CE-6">龍門幣-6(CE-6)</system:String>
|
||||
|
||||
@@ -322,7 +322,11 @@
|
||||
<system:String x:Key="CheckBoxesNotSaved">이 옵션은 한 번만 유효합니다</system:String>
|
||||
<system:String x:Key="CheckBoxesNotSavedAsNull">이 옵션은 우클릭 시 한 번만 적용됩니다</system:String>
|
||||
<system:String x:Key="CheckBoxesNotSavedAsNullInvert">이 옵션은 우클릭 시 한 번만 건너뜁니다</system:String>
|
||||
<system:String x:Key="SpecifiedDropsTip">이 기능은 최적의 스테이지를 자동으로 계산하지 않습니다</system:String>
|
||||
<system:String x:Key="SpecifiedDropsTip" xml:space="preserve">실제로 진입하는 스테이지는 아래의 「{key=StageSelect}」 / 「{key=StageSelect2}」 설정으로 계속 결정됩니다.
|
||||
• {key=Quantity}: 현재 전투 작업에서 해당 재료의 드롭 수를 목표로 삼아, 설정한 수량에 도달하면 멈춥니다.
|
||||
• {key=TargetInventory}: 「{key=Toolbox}-{key=DepotRecognition}」 에 저장된 재고 데이터를 참고하여 설정한 재고 수량까지만 보충합니다.
|
||||
• 재고 데이터는 캐시이므로 수동 파밍, 제작, 재료 사용 후 실제 수량과 달라질 수 있습니다. 「{key=UserDataUpdate}」 또는 「{key=Toolbox}-{key=DepotRecognition}」 에서 동기화해 주세요.
|
||||
• {key=TargetInventory} 사용 중에는 실행 도중 재료와 수량을 바꿀 수 없습니다.</system:String>
|
||||
<system:String x:Key="StartupUpdateCheck">시작 시 업데이트 확인</system:String>
|
||||
<system:String x:Key="UpdateAutoCheck">일정 시간마다 업데이트 확인</system:String>
|
||||
<system:String x:Key="UpdateAutoCheckTip">게임 내 00:00 / 18:00 (서버 현지 시간 04:00 / 22:00)</system:String>
|
||||
@@ -778,6 +782,8 @@ C:\\leidian\\LDPlayer9
|
||||
<system:String x:Key="PerformBattles">횟수 제한</system:String>
|
||||
<system:String x:Key="AssignedMaterial">드롭 제한</system:String>
|
||||
<system:String x:Key="Quantity">드롭 수</system:String>
|
||||
<system:String x:Key="KeepInventory">재고 유지</system:String>
|
||||
<system:String x:Key="TargetInventory">목표 재고</system:String>
|
||||
<system:String x:Key="Series">연속 전투</system:String>
|
||||
<system:String x:Key="SeriesTip" xml:space="preserve">• AUTO:
|
||||
스테이지의 가능한 최대 대리 지휘 횟수의 배수를 감지하고, 자동으로 최대 대리 지휘 횟수를 유지하며, 이성 회복제 사용 후 이성이 남지 않도록 합니다.
|
||||
@@ -807,6 +813,7 @@ C:\\leidian\\LDPlayer9
|
||||
<system:String x:Key="DaysLeftOpen" xml:space="preserve">남은 오픈일 수: </system:String>
|
||||
<system:String x:Key="Inventory">재고</system:String>
|
||||
<system:String x:Key="InventoryUpdateTip">재고 데이터는 「{key=Toolbox}-{key=DepotRecognition}」 을 통해 업데이트 가능</system:String>
|
||||
<system:String x:Key="SpecifiedDropsInventoryUnavailable">작업 "{0}" 에서 {key=TargetInventory} 가 켜져 있지만 현재 사용할 수 있는 재고 데이터가 없어 건너뜁니다. 「{key=Toolbox}-{key=DepotRecognition}」 에서 재고를 업데이트해 주세요.</system:String>
|
||||
<system:String x:Key="LessThanOneDay">1일 미만</system:String>
|
||||
<system:String x:Key="1-7">1-7</system:String>
|
||||
<system:String x:Key="CE-6">CE-6 (용문폐)</system:String>
|
||||
|
||||
@@ -322,7 +322,11 @@
|
||||
<system:String x:Key="CheckBoxesNotSaved">该选项仅生效一次</system:String>
|
||||
<system:String x:Key="CheckBoxesNotSavedAsNull">该选项右键选中时仅生效一次</system:String>
|
||||
<system:String x:Key="CheckBoxesNotSavedAsNullInvert">该选项右键选中时仅跳过一次</system:String>
|
||||
<system:String x:Key="SpecifiedDropsTip">该选项不会自动计算最优关卡</system:String>
|
||||
<system:String x:Key="SpecifiedDropsTip" xml:space="preserve">进入的关卡仍由下方 「{key=StageSelect}」 / 「{key=StageSelect2}」 决定。
|
||||
• {key=Quantity}:以当前理智作战任务中该材料的掉落数量为目标,达到设定数量后停止。
|
||||
• {key=TargetInventory}:会参考 「{key=Toolbox}-{key=DepotRecognition}」 中保存的库存数据,只补到设定库存为止。
|
||||
• 库存数据是缓存值,手动刷关、合成或消耗材料后可能与实际不一致;可通过 「{key=UserDataUpdate}」 或 「{key=Toolbox}-{key=DepotRecognition}」 同步。
|
||||
• 使用 {key=TargetInventory} 时,开始后不能临时修改材料和数量。</system:String>
|
||||
<system:String x:Key="StartupUpdateCheck">启动时检查更新</system:String>
|
||||
<system:String x:Key="UpdateAutoCheck">定时检查更新</system:String>
|
||||
<system:String x:Key="UpdateAutoCheckTip">游戏内 00:00 / 18:00(对应服务器当地时间 04:00 / 22:00)</system:String>
|
||||
@@ -777,6 +781,8 @@ C:\\leidian\\LDPlayer9。\n
|
||||
<system:String x:Key="PerformBattles">指定次数</system:String>
|
||||
<system:String x:Key="AssignedMaterial">指定材料</system:String>
|
||||
<system:String x:Key="Quantity">刷取数量</system:String>
|
||||
<system:String x:Key="KeepInventory">保持库存</system:String>
|
||||
<system:String x:Key="TargetInventory">目标库存</system:String>
|
||||
<system:String x:Key="Series">代理倍率</system:String>
|
||||
<system:String x:Key="SeriesTip" xml:space="preserve">• AUTO:
|
||||
自动识别关卡最大代理倍率, 保持最大代理倍率且使用理智药后理智不溢出
|
||||
@@ -806,6 +812,7 @@ C:\\leidian\\LDPlayer9。\n
|
||||
<system:String x:Key="DaysLeftOpen" xml:space="preserve">剩余天数: </system:String>
|
||||
<system:String x:Key="Inventory">库存</system:String>
|
||||
<system:String x:Key="InventoryUpdateTip">可通过 「{key=Toolbox}-{key=DepotRecognition}」 更新库存数据</system:String>
|
||||
<system:String x:Key="SpecifiedDropsInventoryUnavailable">任务「{0}」启用了 {key=TargetInventory},但当前没有可用的库存数据,已跳过。可通过 「{key=Toolbox}-{key=DepotRecognition}」 更新库存。</system:String>
|
||||
<system:String x:Key="LessThanOneDay">不到 1 天</system:String>
|
||||
<system:String x:Key="1-7">1-7</system:String>
|
||||
<system:String x:Key="CE-6">龙门币-6/5</system:String>
|
||||
|
||||
@@ -322,7 +322,11 @@
|
||||
<system:String x:Key="CheckBoxesNotSaved">該選項僅生效一次</system:String>
|
||||
<system:String x:Key="CheckBoxesNotSavedAsNull">該選項點擊右鍵選取時僅生效一次</system:String>
|
||||
<system:String x:Key="CheckBoxesNotSavedAsNullInvert">該選項點擊右鍵選取時僅跳過一次</system:String>
|
||||
<system:String x:Key="SpecifiedDropsTip">此選項不會自動計算最優關卡</system:String>
|
||||
<system:String x:Key="SpecifiedDropsTip" xml:space="preserve">實際進入的關卡仍由下方 「{key=StageSelect}」 / 「{key=StageSelect2}」 決定。
|
||||
• {key=Quantity}:以目前理智作戰任務中該材料的掉落數量為目標,達到設定數量後停止。
|
||||
• {key=TargetInventory}:會參考 「{key=Toolbox}-{key=DepotRecognition}」 中保存的庫存資料,只補到設定庫存為止。
|
||||
• 庫存資料是快取值,手動刷關、合成或消耗材料後可能與實際不一致;可透過 「{key=UserDataUpdate}」 或 「{key=Toolbox}-{key=DepotRecognition}」 同步。
|
||||
• 使用 {key=TargetInventory} 時,開始後不能臨時修改材料與數量。</system:String>
|
||||
<system:String x:Key="StartupUpdateCheck">啟動時檢查更新</system:String>
|
||||
<system:String x:Key="UpdateAutoCheck">定時檢查更新</system:String>
|
||||
<system:String x:Key="UpdateAutoCheckTip">遊戲內 00:00 / 18:00(對應伺服器當地時間 04:00 / 22:00)</system:String>
|
||||
@@ -777,6 +781,8 @@ C:\\leidian\\LDPlayer9\n
|
||||
<system:String x:Key="PerformBattles">指定次數</system:String>
|
||||
<system:String x:Key="AssignedMaterial">指定材料</system:String>
|
||||
<system:String x:Key="Quantity">刷取數量</system:String>
|
||||
<system:String x:Key="KeepInventory">保持庫存</system:String>
|
||||
<system:String x:Key="TargetInventory">目標庫存</system:String>
|
||||
<system:String x:Key="Series">代理倍率</system:String>
|
||||
<system:String x:Key="SeriesTip" xml:space="preserve">• AUTO:
|
||||
自動辨識關卡最大代理倍率,保持最大代理倍率且使用理智藥後理智不溢出
|
||||
@@ -806,6 +812,7 @@ C:\\leidian\\LDPlayer9\n
|
||||
<system:String x:Key="DaysLeftOpen" xml:space="preserve">剩餘天數: </system:String>
|
||||
<system:String x:Key="Inventory">庫存</system:String>
|
||||
<system:String x:Key="InventoryUpdateTip">可透過 「{key=Toolbox}-{key=DepotRecognition}」 更新庫存數據</system:String>
|
||||
<system:String x:Key="SpecifiedDropsInventoryUnavailable">任務「{0}」啟用了 {key=TargetInventory},但目前沒有可用的庫存資料,已跳過。可透過 「{key=Toolbox}-{key=DepotRecognition}」 更新庫存。</system:String>
|
||||
<system:String x:Key="LessThanOneDay">不到 1 天</system:String>
|
||||
<system:String x:Key="1-7">1-7</system:String>
|
||||
<system:String x:Key="CE-6">龍門幣-6/5</system:String>
|
||||
|
||||
@@ -28,7 +28,7 @@ public class TaskItemViewModel : PropertyChangedBase, IDisposable
|
||||
{
|
||||
_name = name;
|
||||
_isEnable = isCheckedWithNull;
|
||||
Instances.AsstProxy.OnTaskItemStatusChanged += OnTaskStatusChanged;
|
||||
Instances.AsstProxy.OnTaskStatusChanged += OnTaskStatusChanged;
|
||||
}
|
||||
|
||||
private string _name;
|
||||
@@ -130,5 +130,5 @@ public class TaskItemViewModel : PropertyChangedBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
void IDisposable.Dispose() => Instances.AsstProxy.OnTaskItemStatusChanged -= OnTaskStatusChanged;
|
||||
void IDisposable.Dispose() => Instances.AsstProxy.OnTaskStatusChanged -= OnTaskStatusChanged;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using JetBrains.Annotations;
|
||||
@@ -27,6 +29,7 @@ using MaaWpfGui.Extensions;
|
||||
using MaaWpfGui.Helper;
|
||||
using MaaWpfGui.Models;
|
||||
using MaaWpfGui.Models.AsstTasks;
|
||||
using MaaWpfGui.States;
|
||||
using MaaWpfGui.Utilities;
|
||||
using MaaWpfGui.Utilities.ValueType;
|
||||
using MaaWpfGui.ViewModels.UI;
|
||||
@@ -44,6 +47,9 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
{
|
||||
public const string AnnihilationName = "Annihilation";
|
||||
private static readonly ILogger _logger = Log.ForContext<FightSettingsUserControlModel>();
|
||||
private readonly RunningState _runningState;
|
||||
private readonly Lock _inventoryTargetRuntimeStateLock = new();
|
||||
private Dictionary<int, InventoryTargetRuntimeState> _inventoryTargetRuntimeStateByTaskId = [];
|
||||
|
||||
public static FightTimes? FightReport { get; set; }
|
||||
|
||||
@@ -56,6 +62,15 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
|
||||
public FightSettingsUserControlModel()
|
||||
{
|
||||
_runningState = RunningState.Instance;
|
||||
_runningState.StateChanged += OnRunningStateChanged;
|
||||
Instances.AsstProxy.OnTaskStatusChanged += OnTaskStatusChanged;
|
||||
|
||||
if (Instances.ToolboxViewModel is { } toolboxViewModel)
|
||||
{
|
||||
toolboxViewModel.DepotResult.CollectionChanged += OnDepotResultCollectionChanged;
|
||||
}
|
||||
|
||||
foreach (var i in WeeklyScheduleSource)
|
||||
{
|
||||
i.PropertyChanged += (_, __) => SaveWeeklySchedule();
|
||||
@@ -66,15 +81,92 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
InitDrops();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当队列进入或离开空闲状态时,清理按任务保存的目标库存运行时状态。
|
||||
/// </summary>
|
||||
private void OnRunningStateChanged(object? sender, RunningState.RunningStateChangedEventArgs e)
|
||||
{
|
||||
if (e.NewState.Idle == e.OldState.Idle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResetInventoryTargetRuntimeState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置各作战任务启动时捕获的目标库存运行时缓存。
|
||||
/// </summary>
|
||||
private void ResetInventoryTargetRuntimeState()
|
||||
{
|
||||
lock (_inventoryTargetRuntimeStateLock)
|
||||
{
|
||||
_inventoryTargetRuntimeStateByTaskId = [];
|
||||
}
|
||||
|
||||
Execute.OnUIThread(NotifySpecifiedDropsStateChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当作战任务进入进行中状态时,仅捕获一次目标库存值,并在需要时刷新当前选中的面板。
|
||||
/// </summary>
|
||||
private void OnTaskStatusChanged(int taskId, TaskItemStatus status)
|
||||
{
|
||||
if (status != TaskItemStatus.InProgress || taskId <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetFightTaskByTaskId(taskId) is { } startedFight &&
|
||||
IsInventoryTargetDropEnabled(startedFight) &&
|
||||
GetInventoryTargetRuntimeState(taskId) == null)
|
||||
{
|
||||
SerializeTask(startedFight, taskId);
|
||||
}
|
||||
|
||||
Execute.OnUIThread(() => {
|
||||
if (TaskSettingVisibilityInfo.CurrentTask is not FightTask currentFight || !IsInventoryTargetDropEnabled(currentFight))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int currentIndex = TaskSettingVisibilityInfo.Instance.CurrentIndex;
|
||||
if (currentIndex < 0 || currentIndex >= Instances.TaskQueueViewModel.TaskItemViewModels.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Instances.TaskQueueViewModel.TaskItemViewModels[currentIndex].TaskIds.Contains(taskId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NotifySpecifiedDropsStateChanged();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当仓库识别数据变化时,刷新界面显示的目标库存信息。
|
||||
/// </summary>
|
||||
private void OnDepotResultCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (!_runningState.Idle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Execute.OnUIThread(NotifySpecifiedDropsStateChanged);
|
||||
}
|
||||
|
||||
public static FightSettingsUserControlModel Instance { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a stage plan item is being dragged.
|
||||
/// Gets or sets a value indicating whether 关卡规划项正在被拖拽。
|
||||
/// </summary>
|
||||
public bool IsDragging { get => field; set => SetAndNotify(ref field, value); }
|
||||
public bool IsStageItemDragging { get => field; set => SetAndNotify(ref field, value); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or private sets the list of stages.
|
||||
/// Gets or private sets a value indicating whether 关卡列表。
|
||||
/// </summary>
|
||||
public ObservableCollection<StageSourceItem> StageListSource { get => field; private set => SetAndNotify(ref field, value); } = [];
|
||||
|
||||
@@ -123,7 +215,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use custom stage code.
|
||||
/// Gets or sets a value indicating whether 使用自定义关卡代码。
|
||||
/// </summary>
|
||||
public bool CustomStageCode
|
||||
{
|
||||
@@ -148,9 +240,9 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset unsaved battle parameters.
|
||||
/// 重置未保存的作战参数。
|
||||
/// </summary>
|
||||
/// <param name="fight">The fight task.</param>
|
||||
/// <param name="fight">作战任务配置。</param>
|
||||
public static void ResetFightVariables(FightTask? fight)
|
||||
{
|
||||
fight?.UseStone ??= false;
|
||||
@@ -160,7 +252,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use medicine with null.
|
||||
/// Gets or sets a value indicating whether 使用理智药。
|
||||
/// </summary>
|
||||
public bool? UseMedicine
|
||||
{
|
||||
@@ -181,7 +273,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the amount of medicine used.
|
||||
/// Gets or sets 使用理智药数量。
|
||||
/// </summary>
|
||||
public int MedicineNumber
|
||||
{
|
||||
@@ -199,7 +291,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
public static string UseStoneString => LocalizationHelper.GetString("UseOriginitePrime");
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use originiums with null.
|
||||
/// Gets or sets a value indicating whether 使用源石。
|
||||
/// </summary>
|
||||
public bool? UseStone
|
||||
{
|
||||
@@ -225,7 +317,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use originiums.
|
||||
/// Gets or sets a value indicating whether 使用源石 with null
|
||||
/// </summary>
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
[PropertyDependsOn(nameof(UseStone))]
|
||||
@@ -236,7 +328,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the amount of originiums used.
|
||||
/// Gets or sets 使用源石数量。
|
||||
/// </summary>
|
||||
public int StoneNumber
|
||||
{
|
||||
@@ -252,7 +344,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the number of times is limited with null.
|
||||
/// Gets or sets a value indicating whether 限制次数 with null
|
||||
/// </summary>
|
||||
public bool? HasTimesLimited
|
||||
{
|
||||
@@ -268,7 +360,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the max number of times.
|
||||
/// Gets or sets 最大次数。
|
||||
/// </summary>
|
||||
public int MaxTimes
|
||||
{
|
||||
@@ -296,7 +388,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the max number of times.
|
||||
/// Gets or sets 连战次数。
|
||||
/// </summary>
|
||||
public int Series
|
||||
{
|
||||
@@ -314,12 +406,17 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
#region Drops
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the drops are specified.
|
||||
/// Gets or sets a value indicating whether 启用指定材料。
|
||||
/// </summary>
|
||||
public bool? IsSpecifiedDrops
|
||||
{
|
||||
get => GetTaskConfig<FightTask>().EnableTargetDrop;
|
||||
set {
|
||||
if (IsSpecifiedInventoryLocked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SetTaskConfig<FightTask>(t => t.EnableTargetDrop == value, t => t.EnableTargetDrop = value))
|
||||
{
|
||||
return;
|
||||
@@ -330,7 +427,245 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of all drops.
|
||||
/// Gets or sets a value indicating whether 指定材料按目标库存模式计算。
|
||||
/// </summary>
|
||||
public bool UseInventoryTarget
|
||||
{
|
||||
get => GetTaskConfig<FightTask>().IsInventoryTarget;
|
||||
set {
|
||||
if (!_runningState.Idle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SetTaskConfig<FightTask>(t => t.IsInventoryTarget == value, t => t.IsInventoryTarget = value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NotifySpecifiedDropsStateChanged();
|
||||
SetFightParams();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSpecifiedInventoryLocked => UseInventoryTarget && !_runningState.Idle;
|
||||
|
||||
public bool UseDropQuantityMode
|
||||
{
|
||||
get => !UseInventoryTarget;
|
||||
set {
|
||||
if (value)
|
||||
{
|
||||
UseInventoryTarget = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseTargetInventoryMode
|
||||
{
|
||||
get => UseInventoryTarget;
|
||||
set {
|
||||
if (value)
|
||||
{
|
||||
UseInventoryTarget = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string CurrentDropsInventoryText => FormatSpecifiedDropsCount(GetSpecifiedDropsInventoryCount(GetTaskConfig<FightTask>(), GetCurrentFightTaskId()));
|
||||
|
||||
public string EffectiveDropsQuantityText => FormatSpecifiedDropsCount(GetSpecifiedDropsCoreQuantity(GetTaskConfig<FightTask>(), GetCurrentFightTaskId()));
|
||||
|
||||
private static string FormatSpecifiedDropsCount(int? count) => count is int value ? value.FormatNumber(false) : "--";
|
||||
|
||||
private static bool IsInventoryTargetDropEnabled(FightTask fight)
|
||||
{
|
||||
return fight.EnableTargetDrop != false && !string.IsNullOrEmpty(fight.DropId) && fight.IsInventoryTarget;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取当前识别到的仓库数量,并转换为查询表。
|
||||
/// </summary>
|
||||
private static Dictionary<string, int>? GetCurrentInventoryCounts()
|
||||
{
|
||||
var depotResult = Instances.ToolboxViewModel?.DepotResult;
|
||||
if (depotResult == null || depotResult.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var snapshot = depotResult
|
||||
.Where(item => item.Count >= 0)
|
||||
.ToDictionary(item => item.Id, item => item.Count);
|
||||
|
||||
return snapshot.Count > 0 ? snapshot : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前选中作战任务绑定的第一个 core task id。
|
||||
/// </summary>
|
||||
private static int? GetCurrentFightTaskId()
|
||||
{
|
||||
if (TaskSettingVisibilityInfo.CurrentTask is not FightTask fight)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = ConfigFactory.CurrentConfig.TaskQueue.IndexOf(fight);
|
||||
if (index < 0 || index >= Instances.TaskQueueViewModel.TaskItemViewModels.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var taskIds = Instances.TaskQueueViewModel.TaskItemViewModels[index].TaskIds;
|
||||
return taskIds.Count > 0 ? taskIds[0] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据 core task id 反查对应的作战任务配置。
|
||||
/// </summary>
|
||||
private static FightTask? GetFightTaskByTaskId(int taskId)
|
||||
{
|
||||
if (taskId <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int index = 0; index < Instances.TaskQueueViewModel.TaskItemViewModels.Count; ++index)
|
||||
{
|
||||
if (!Instances.TaskQueueViewModel.TaskItemViewModels[index].TaskIds.Contains(taskId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return index < ConfigFactory.CurrentConfig.TaskQueue.Count
|
||||
? ConfigFactory.CurrentConfig.TaskQueue[index] as FightTask
|
||||
: null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查指定 core 作战任务是否已在运行。
|
||||
/// </summary>
|
||||
private static bool IsTaskInProgress(int taskId)
|
||||
{
|
||||
return taskId > 0 &&
|
||||
Instances.AsstProxy.TasksStatus.TryGetValue(taskId, out var taskState) &&
|
||||
taskState.Status == MaaWpfGui.Main.TaskStatus.InProgress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定 core 任务缓存的目标库存运行时状态。
|
||||
/// </summary>
|
||||
private InventoryTargetRuntimeState? GetInventoryTargetRuntimeState(int? taskId)
|
||||
{
|
||||
if (taskId is not int id || id <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
lock (_inventoryTargetRuntimeStateLock)
|
||||
{
|
||||
return _inventoryTargetRuntimeStateByTaskId.GetValueOrDefault(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定掉落目标显示的库存数量。任务开始后,该显示值会被冻结。
|
||||
/// </summary>
|
||||
private int? GetSpecifiedDropsInventoryCount(FightTask fight, int? taskId = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fight.DropId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (GetInventoryTargetRuntimeState(taskId) is { } runtimeState && runtimeState.DropId == fight.DropId)
|
||||
{
|
||||
return runtimeState.StartInventory;
|
||||
}
|
||||
|
||||
var currentInventoryCounts = GetCurrentInventoryCounts();
|
||||
if (currentInventoryCounts == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return currentInventoryCounts.TryGetValue(fight.DropId, out var count) ? count : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定目标库存任务显示并下发给 core 的实际刷取数量。
|
||||
/// </summary>
|
||||
private int? GetSpecifiedDropsCoreQuantity(FightTask fight, int? taskId = null)
|
||||
{
|
||||
if (!IsInventoryTargetDropEnabled(fight))
|
||||
{
|
||||
return fight.DropCount;
|
||||
}
|
||||
|
||||
if (GetInventoryTargetRuntimeState(taskId) is { } runtimeState)
|
||||
{
|
||||
return runtimeState.EffectiveQuantity;
|
||||
}
|
||||
|
||||
int? inventoryCount = GetSpecifiedDropsInventoryCount(fight);
|
||||
if (inventoryCount is not int currentInventory)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Math.Max(fight.DropCount - currentInventory, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 捕获运行中作战任务需要保持固定的目标库存值。
|
||||
/// </summary>
|
||||
private static InventoryTargetRuntimeState? CreateInventoryTargetRuntimeState(FightTask fight)
|
||||
{
|
||||
var currentInventoryCounts = GetCurrentInventoryCounts();
|
||||
if (currentInventoryCounts == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int startInventory = currentInventoryCounts.GetValueOrDefault(fight.DropId);
|
||||
int effectiveQuantity = Math.Max(fight.DropCount - startInventory, 0);
|
||||
return new(fight.DropId, startInventory, effectiveQuantity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在捕获启动时的目标库存值后,保存该 core 作战任务的运行时状态。
|
||||
/// </summary>
|
||||
private void RememberInventoryTargetRuntimeState(int taskId, InventoryTargetRuntimeState runtimeState)
|
||||
{
|
||||
if (taskId <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_inventoryTargetRuntimeStateLock)
|
||||
{
|
||||
_inventoryTargetRuntimeStateByTaskId[taskId] = runtimeState;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新目标库存模式及其显示数量相关的 UI 绑定。
|
||||
/// </summary>
|
||||
private void NotifySpecifiedDropsStateChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsSpecifiedInventoryLocked));
|
||||
NotifyOfPropertyChange(nameof(UseDropQuantityMode));
|
||||
NotifyOfPropertyChange(nameof(UseTargetInventoryMode));
|
||||
NotifyOfPropertyChange(nameof(CurrentDropsInventoryText));
|
||||
NotifyOfPropertyChange(nameof(EffectiveDropsQuantityText));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets 全部掉落材料列表。
|
||||
/// </summary>
|
||||
private List<CombinedData> AllDrops { get; } = [];
|
||||
|
||||
@@ -389,24 +724,34 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or private sets the list of drops.
|
||||
/// Gets 获取或私有设置掉落材料列表。
|
||||
/// </summary>
|
||||
public ObservableCollection<CombinedData> DropsList { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item ID of drops.
|
||||
/// Gets or sets 指定掉落材料 ID。
|
||||
/// </summary>
|
||||
public string DropsItemId
|
||||
{
|
||||
get => GetTaskConfig<FightTask>().DropId;
|
||||
set {
|
||||
SetTaskConfig<FightTask>(t => t.DropId == value, t => t.DropId = value);
|
||||
if (IsSpecifiedInventoryLocked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SetTaskConfig<FightTask>(t => t.DropId == value, t => t.DropId = value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NotifySpecifiedDropsStateChanged();
|
||||
SetFightParams();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item Name of drops.
|
||||
/// Gets or sets 指定掉落材料名称。
|
||||
/// </summary>
|
||||
public string DropsItemName { get => field; set => SetAndNotify(ref field, value); } = string.Empty;
|
||||
|
||||
@@ -414,6 +759,13 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
[UsedImplicitly]
|
||||
public void DropsListDropDownClosed()
|
||||
{
|
||||
if (IsSpecifiedInventoryLocked)
|
||||
{
|
||||
RefreshDropName();
|
||||
NotifySpecifiedDropsStateChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
if (DropsList.FirstOrDefault(i => i.Display == DropsItemName) is { } item)
|
||||
{
|
||||
DropsItemId = item.Value;
|
||||
@@ -427,13 +779,23 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the quantity of drops.
|
||||
/// Gets or sets 指定掉落数量。
|
||||
/// </summary>
|
||||
public int DropsQuantity
|
||||
{
|
||||
get => GetTaskConfig<FightTask>().DropCount;
|
||||
set {
|
||||
SetTaskConfig<FightTask>(t => t.DropCount == value, t => t.DropCount = value);
|
||||
if (IsSpecifiedInventoryLocked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SetTaskConfig<FightTask>(t => t.DropCount == value, t => t.DropCount = value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NotifySpecifiedDropsStateChanged();
|
||||
SetFightParams();
|
||||
}
|
||||
}
|
||||
@@ -487,7 +849,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use DrGrandet mode.
|
||||
/// Gets or sets a value indicating whether 启用 DrGrandet 模式。
|
||||
/// </summary>
|
||||
public bool IsDrGrandet
|
||||
{
|
||||
@@ -496,7 +858,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use alternate stage.
|
||||
/// Gets or sets a value indicating whether 使用备选关卡。
|
||||
/// </summary>
|
||||
public bool UseAlternateStage
|
||||
{
|
||||
@@ -593,7 +955,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
public string ActivityInfo { get => field; private set => SetAndNotify(ref field, value); } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to hide unavailable stages.
|
||||
/// Gets or sets a value indicating whether 隐藏未开放关卡。
|
||||
/// </summary>
|
||||
public bool HideUnavailableStage
|
||||
{
|
||||
@@ -627,7 +989,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to hide series.
|
||||
/// Gets or sets a value indicating whether 隐藏连战设置。
|
||||
/// </summary>
|
||||
public bool HideSeries
|
||||
{
|
||||
@@ -636,7 +998,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use weekly schedule.
|
||||
/// Gets or sets a value indicating whether 使用周计划。
|
||||
/// </summary>
|
||||
public bool UseWeeklySchedule
|
||||
{
|
||||
@@ -711,6 +1073,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
RefreshCurrentStagePlan();
|
||||
RefreshWeeklySchedule();
|
||||
RefreshDropName();
|
||||
NotifySpecifiedDropsStateChanged();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
@@ -733,7 +1096,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
#region 关卡列表更新
|
||||
|
||||
/// <summary>
|
||||
/// Updates stage list.
|
||||
/// 更新关卡列表。
|
||||
/// 使用手动输入时,只更新关卡列表,不更新关卡选择
|
||||
/// 使用隐藏当日不开放时,更新关卡列表,关卡选择为未开放的关卡时清空
|
||||
/// 使用备选关卡时,更新关卡列表,关卡选择为未开放的关卡时在关卡列表中添加对应未开放关卡,避免清空导致进入上次关卡
|
||||
@@ -741,8 +1104,8 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
/// 除手动输入外所有情况下,如果剩余理智为未开放的关卡,会被清空
|
||||
/// </summary>
|
||||
/// <returns>更新任务列表的Task</returns>
|
||||
// FIXME: 被注入对象只能在private函数内使用,只有Model显示之后才会被注入。如果Model还没有触发OnInitialActivate时调用函数会NullPointerException
|
||||
// 这个函数被列为public可见,意味着他注入对象前被调用
|
||||
// FIXME:被注入对象只能在 private 函数内使用,只有 Model 显示之后才会被注入。如果 Model 还没有触发 OnInitialActivate 时调用此函数,会导致空引用异常。
|
||||
// 这个函数被声明为 public,意味着它可能会在注入对象前被调用。
|
||||
public Task UpdateStageList()
|
||||
{
|
||||
return Execute.OnUIThreadAsync(async () => {
|
||||
@@ -938,7 +1301,7 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
public bool IsVisible { get => field; set => SetAndNotify(ref field, value); } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 过期活动关卡, 加删除线
|
||||
/// Gets or sets a value indicating whether 关卡已过期并显示删除线。
|
||||
/// </summary>
|
||||
public bool IsOutdated { get; set; } = false;
|
||||
}
|
||||
@@ -1028,12 +1391,52 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
var yjTime = DateTimeOffset.Now.ToYjDateTime().ToLocalTime();
|
||||
var daysUntilEndOfWeek = ((7 - (int)yjTime.DayOfWeek + 7) % 7) + 1; // 距离本周结束的天数, 用鹰历计算
|
||||
var activityExpireDays = activityExpireIn2Days && fight.UseExpireMedicineForActivity ? daysUntilEndOfWeek : 0;
|
||||
InventoryTargetRuntimeState? inventoryTargetRuntimeState = null;
|
||||
bool shouldRememberInventoryTargetRuntimeState = false;
|
||||
int specifiedDropsQuantity = fight.DropCount;
|
||||
|
||||
if (IsInventoryTargetDropEnabled(fight))
|
||||
{
|
||||
if (taskId is int existingTaskId and > 0 && Instance.GetInventoryTargetRuntimeState(existingTaskId) is { } existingRuntimeState)
|
||||
{
|
||||
inventoryTargetRuntimeState = existingRuntimeState;
|
||||
}
|
||||
else
|
||||
{
|
||||
inventoryTargetRuntimeState = CreateInventoryTargetRuntimeState(fight);
|
||||
if (taskId is int startedTaskId and > 0 && inventoryTargetRuntimeState != null && IsTaskInProgress(startedTaskId))
|
||||
{
|
||||
shouldRememberInventoryTargetRuntimeState = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (inventoryTargetRuntimeState == null)
|
||||
{
|
||||
Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetStringFormat("SpecifiedDropsInventoryUnavailable", fight.NameDisplay), UiLogColor.Warning);
|
||||
return (null, []);
|
||||
}
|
||||
|
||||
specifiedDropsQuantity = inventoryTargetRuntimeState.EffectiveQuantity;
|
||||
if (specifiedDropsQuantity <= 0 && taskId is null)
|
||||
{
|
||||
return (null, []);
|
||||
}
|
||||
}
|
||||
|
||||
var effectiveMaxTimes = fight.EnableTimesLimit != false ? fight.TimesLimit : int.MaxValue;
|
||||
|
||||
// 任务运行时如果启用了目标掉落且指定的掉落数量小于等于 0,则将最大挑战次数设为 0,防止任务继续运行但无法获得目标掉落
|
||||
if (taskId is int and > 0 && IsInventoryTargetDropEnabled(fight) && specifiedDropsQuantity <= 0)
|
||||
{
|
||||
effectiveMaxTimes = 0;
|
||||
}
|
||||
|
||||
var task = new AsstFightTask() {
|
||||
Stage = stage,
|
||||
Medicine = fight.UseMedicine != false ? fight.MedicineCount : 0,
|
||||
Stone = fight.UseStone != false ? fight.StoneCount : 0,
|
||||
Series = fight.Series,
|
||||
MaxTimes = fight.EnableTimesLimit != false ? fight.TimesLimit : int.MaxValue,
|
||||
MaxTimes = effectiveMaxTimes,
|
||||
MedicineExpireDays = Math.Max(expireDays, activityExpireDays),
|
||||
IsDrGrandet = fight.IsDrGrandet,
|
||||
ReportToPenguin = SettingsViewModel.GameSettings.EnablePenguin,
|
||||
@@ -1049,21 +1452,36 @@ public class FightSettingsUserControlModel : TaskSettingsViewModel, FightSetting
|
||||
task.Stage = fight.AnnihilationStage;
|
||||
}
|
||||
|
||||
if (fight.EnableTargetDrop != false && !string.IsNullOrEmpty(fight.DropId))
|
||||
if (fight.EnableTargetDrop != false && !string.IsNullOrEmpty(fight.DropId) && specifiedDropsQuantity > 0)
|
||||
{
|
||||
task.Drops.Add(fight.DropId, fight.DropCount);
|
||||
task.Drops.Add(fight.DropId, specifiedDropsQuantity);
|
||||
}
|
||||
|
||||
if (fight.EnableTimesLimit is not false && fight.Series > 0 && fight.TimesLimit % fight.Series != 0)
|
||||
if (effectiveMaxTimes > 0 && effectiveMaxTimes < int.MaxValue && fight.Series > 0 && effectiveMaxTimes % fight.Series != 0)
|
||||
{
|
||||
Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetStringFormat("FightTimesMayNotExhausted", fight.TimesLimit, fight.Series), UiLogColor.Warning);
|
||||
Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetStringFormat("FightTimesMayNotExhausted", effectiveMaxTimes, fight.Series), UiLogColor.Warning);
|
||||
}
|
||||
|
||||
return taskId switch {
|
||||
int id when id > 0 => (Instances.AsstProxy.AsstSetTaskParamsEncoded(id, task), [id]),
|
||||
null => FromSingle(Instances.AsstProxy.AsstAppendTaskWithEncoding(TaskType.Fight, task)),
|
||||
_ => (null, []),
|
||||
};
|
||||
if (taskId is int id and > 0)
|
||||
{
|
||||
bool updated = Instances.AsstProxy.AsstSetTaskParamsEncoded(id, task);
|
||||
if (updated && shouldRememberInventoryTargetRuntimeState && inventoryTargetRuntimeState != null)
|
||||
{
|
||||
Instance.RememberInventoryTargetRuntimeState(id, inventoryTargetRuntimeState);
|
||||
}
|
||||
|
||||
return (updated, [id]);
|
||||
}
|
||||
|
||||
if (taskId is null)
|
||||
{
|
||||
var appendResult = Instances.AsstProxy.AsstAppendTaskWithEncoding(TaskType.Fight, task);
|
||||
return (appendResult.IsSuccess, appendResult.TaskId > 0 ? [appendResult.TaskId] : []);
|
||||
}
|
||||
|
||||
return (null, []);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record InventoryTargetRuntimeState(string DropId, int StartInventory, int EffectiveQuantity);
|
||||
}
|
||||
|
||||
@@ -100,45 +100,67 @@
|
||||
<ColumnDefinition Width="Auto" MinWidth="100" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid Margin="6">
|
||||
<CheckBox
|
||||
Height="30"
|
||||
VerticalContentAlignment="Center"
|
||||
Content="{DynamicResource AssignedMaterial}"
|
||||
IsChecked="{Binding IsSpecifiedDrops}"
|
||||
MouseRightButtonUp="{s:Action ToggleCheckBoxNullOnRightClick,
|
||||
Target={x:Type helper:CheckBoxHelper}}"
|
||||
ToolTip="{DynamicResource CheckBoxesNotSavedAsNull}" />
|
||||
<controls:TooltipBlock
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
NormalOpacity="0.25"
|
||||
TooltipText="{DynamicResource SpecifiedDropsTip}" />
|
||||
</Grid>
|
||||
<Grid Height="42" Visibility="{c:Binding IsSpecifiedDrops}">
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{DynamicResource Quantity}"
|
||||
TextAlignment="Center" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Orientation="Vertical">
|
||||
<ComboBox
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="6">
|
||||
<CheckBox
|
||||
Height="30"
|
||||
Margin="6"
|
||||
VerticalContentAlignment="Center"
|
||||
DisplayMemberPath="Display"
|
||||
DropDownClosed="{s:Action DropsListDropDownClosed}"
|
||||
IsEditable="True"
|
||||
IsTextSearchEnabled="False"
|
||||
ItemsSource="{Binding DropsList}"
|
||||
Loaded="{s:Action MakeComboBoxSearchable,
|
||||
Target={x:Type ui_vms:SettingsViewModel}}"
|
||||
SelectedValue="{Binding DropsItemId}"
|
||||
SelectedValuePath="Value"
|
||||
Text="{Binding DropsItemName}" />
|
||||
Content="{DynamicResource AssignedMaterial}"
|
||||
IsChecked="{Binding IsSpecifiedDrops}"
|
||||
IsEnabled="{c:Binding !IsSpecifiedInventoryLocked}"
|
||||
MouseRightButtonUp="{s:Action ToggleCheckBoxNullOnRightClick,
|
||||
Target={x:Type helper:CheckBoxHelper}}"
|
||||
ToolTip="{DynamicResource CheckBoxesNotSavedAsNull}" />
|
||||
<controls:TooltipBlock
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
NormalOpacity="0.25"
|
||||
TooltipText="{DynamicResource SpecifiedDropsTip}" />
|
||||
</Grid>
|
||||
<hc:ButtonGroup
|
||||
Grid.Row="1"
|
||||
MaxWidth="88"
|
||||
Margin="6,6,6,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{c:Binding !IsSpecifiedInventoryLocked}"
|
||||
Orientation="Vertical"
|
||||
Visibility="{c:Binding IsSpecifiedDrops}">
|
||||
<RadioButton
|
||||
hc:VisualElement.HighlightBackground="{DynamicResource RegionBrushOpacity25}"
|
||||
Background="{DynamicResource RegionBrushOpacity25}"
|
||||
Content="{DynamicResource Quantity}"
|
||||
IsChecked="{Binding UseDropQuantityMode}" />
|
||||
<RadioButton
|
||||
hc:VisualElement.HighlightBackground="{DynamicResource RegionBrushOpacity25}"
|
||||
Background="{DynamicResource RegionBrushOpacity25}"
|
||||
Content="{DynamicResource TargetInventory}"
|
||||
IsChecked="{Binding UseTargetInventoryMode}" />
|
||||
</hc:ButtonGroup>
|
||||
<ComboBox
|
||||
Grid.Column="1"
|
||||
Height="30"
|
||||
Margin="6"
|
||||
VerticalContentAlignment="Center"
|
||||
DisplayMemberPath="Display"
|
||||
DropDownClosed="{s:Action DropsListDropDownClosed}"
|
||||
IsEditable="True"
|
||||
IsEnabled="{c:Binding !IsSpecifiedInventoryLocked}"
|
||||
IsTextSearchEnabled="False"
|
||||
ItemsSource="{Binding DropsList}"
|
||||
Loaded="{s:Action MakeComboBoxSearchable,
|
||||
Target={x:Type ui_vms:SettingsViewModel}}"
|
||||
SelectedValue="{Binding DropsItemId}"
|
||||
SelectedValuePath="Value"
|
||||
Text="{Binding DropsItemName}" />
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Vertical">
|
||||
<hc:NumericUpDown
|
||||
Height="30"
|
||||
MinWidth="60"
|
||||
@@ -146,10 +168,24 @@
|
||||
HorizontalAlignment="Left"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
IsEnabled="{c:Binding !IsSpecifiedInventoryLocked}"
|
||||
Maximum="1145141919"
|
||||
Minimum="1"
|
||||
Visibility="{c:Binding IsSpecifiedDrops}"
|
||||
Value="{Binding DropsQuantity, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<StackPanel Margin="6,2,6,0" Visibility="{c:Binding 'IsSpecifiedDrops and UseInventoryTarget'}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<controls:TextBlock Text="{DynamicResource Inventory}" />
|
||||
<controls:TextBlock Margin="4,0,0,0" Text=":" />
|
||||
<controls:TextBlock Margin="4,0,0,0" Text="{Binding CurrentDropsInventoryText}" />
|
||||
<controls:TooltipBlock Margin="4,0,0,0" TooltipText="{DynamicResource InventoryUpdateTip}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,2,0,0" Orientation="Horizontal">
|
||||
<controls:TextBlock Text="{DynamicResource Quantity}" />
|
||||
<controls:TextBlock Margin="4,0,0,0" Text=":" />
|
||||
<controls:TextBlock Margin="4,0,0,0" Text="{Binding EffectiveDropsQuantityText}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid Grid.Row="2" Visibility="{c:Binding !HideSeries}">
|
||||
@@ -310,7 +346,7 @@
|
||||
<TextBox
|
||||
Height="30"
|
||||
hc:InfoElement.Placeholder="{DynamicResource DefaultStage}"
|
||||
IsEnabled="{c:Binding !IsDragging,
|
||||
IsEnabled="{c:Binding !IsStageItemDragging,
|
||||
Source={x:Static ui_vms:TaskQueueViewModel.FightTask}}"
|
||||
Opacity="{c:Binding 'IsOpen ? 1 : 0.5'}"
|
||||
Text="{Binding Stage, UpdateSourceTrigger=PropertyChanged}"
|
||||
|
||||
@@ -32,7 +32,7 @@ public partial class FightSettingsUserControl : System.Windows.Controls.UserCont
|
||||
|
||||
private void DragHandle_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
TaskQueueViewModel.FightTask.IsDragging = true;
|
||||
TaskQueueViewModel.FightTask.IsStageItemDragging = true;
|
||||
|
||||
// 拖拽松开时可能不经过 Button,在 Window 层兜底恢复
|
||||
var window = Window.GetWindow(this);
|
||||
@@ -41,12 +41,12 @@ public partial class FightSettingsUserControl : System.Windows.Controls.UserCont
|
||||
|
||||
private void DragHandle_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
TaskQueueViewModel.FightTask.IsDragging = false;
|
||||
TaskQueueViewModel.FightTask.IsStageItemDragging = false;
|
||||
}
|
||||
|
||||
private void Window_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
TaskQueueViewModel.FightTask.IsDragging = false;
|
||||
TaskQueueViewModel.FightTask.IsStageItemDragging = false;
|
||||
if (sender is Window window)
|
||||
{
|
||||
window.PreviewMouseLeftButtonUp -= Window_PreviewMouseLeftButtonUp;
|
||||
|
||||
Reference in New Issue
Block a user