mirror of
https://github.com/waynebian01/Shigure.git
synced 2026-09-03 07:15:24 +08:00
- Updated UiCardPanel to use UiTheme.CardCornerRadius for consistent corner styling. - Modified UiPillTab to enable TabStop and set AccessibleRole to PageTab, enhancing keyboard navigation. - Implemented keyboard handling in UiPillTab for Enter and Space keys to trigger click events. - Enhanced focus visuals in UiPillTab with focus rectangle drawing. - Introduced new constants in UiTheme for CardPadding, PageGap, ActionButtonHeight, GridRowHeight, TabBarHeight, CardCornerRadius, and ControlCornerRadius for better layout management. - Updated UnitEditorForm to utilize new theming constants and improved layout with UiCardPanel for better visual structure. - Refined action button styling in UnitEditorForm for consistent button sizes and margins. - Adjusted labeled row height in UnitEditorForm for better alignment.
1388 lines
51 KiB
C#
1388 lines
51 KiB
C#
using System.Drawing;
|
||
|
||
namespace Shigure;
|
||
|
||
/// <summary>
|
||
/// 动态单位 / 数量字段的编辑弹窗: 选择类别(单位/数量)与选择器, 按所选类型动态显隐参数控件。
|
||
/// 光环候选来自当前职业/专精的 group 字段。校验名称非空、唯一、非纯数字、不含 '.'/'$'。
|
||
/// </summary>
|
||
public sealed class UnitEditorForm : Form
|
||
{
|
||
private const int RowWidth = 800;
|
||
private const int LabelWidth = 132;
|
||
private const int ControlLeft = LabelWidth + 10;
|
||
|
||
private static readonly RoleOption[] RoleOptions =
|
||
[
|
||
new("坦克 (1)", 1),
|
||
new("治疗 (2)", 2),
|
||
new("输出 (3)", 3)
|
||
];
|
||
|
||
private static readonly DispelTypeOption[] DispelTypeOptions =
|
||
[
|
||
new("1: 魔法", 1),
|
||
new("2: 诅咒", 2),
|
||
new("3: 疾病", 3),
|
||
new("4: 中毒", 4)
|
||
];
|
||
|
||
private static readonly LowestHealthAuraFilterItem[] LowestHealthAuraFilterOptions =
|
||
[
|
||
new("不筛选光环", LowestHealthAuraFilterKind.None),
|
||
new("带任一光环", LowestHealthAuraFilterKind.WithAnyAura),
|
||
new("不带任一光环", LowestHealthAuraFilterKind.WithoutAnyAura),
|
||
new("不带某光环", LowestHealthAuraFilterKind.WithoutAura),
|
||
new("带某光环", LowestHealthAuraFilterKind.WithAura),
|
||
new("某光环值等于", LowestHealthAuraFilterKind.WithAuraCount)
|
||
];
|
||
|
||
private static readonly LowestHealthRoleFilterItem[] LowestHealthRoleFilterOptions =
|
||
[
|
||
new("不筛选职责", null),
|
||
new("包含某职责", UnitRoleFilterKind.Include),
|
||
new("不含某职责", UnitRoleFilterKind.Exclude)
|
||
];
|
||
|
||
private static readonly SelectorItem[] UnitSelectors =
|
||
[
|
||
new("生命值最低", UnitSelectorKind.LowestHealth),
|
||
new("治疗吸收最高", UnitSelectorKind.HighestHealingAbsorb),
|
||
new("按职责", UnitSelectorKind.UnitWithRole),
|
||
new("按职责且不带某光环", UnitSelectorKind.UnitWithRoleWithoutAura),
|
||
new("带某光环(持续最久)", UnitSelectorKind.UnitWithAura),
|
||
new("带某驱散类型", UnitSelectorKind.UnitWithDispelType)
|
||
];
|
||
|
||
private static readonly CountItem[] CountSelectors =
|
||
[
|
||
new("血量 - 低于阈值", CountKind.UnitsBelowHealth),
|
||
new("血量 - 低于阈值不带某光环", CountKind.UnitsWithoutAuraBelowHealth),
|
||
new("血量 - 低于阈值带某光环", CountKind.UnitsWithAuraBelowHealth),
|
||
new("治疗吸收 - 大于阈值", CountKind.UnitsAboveHealingAbsorb),
|
||
new("治疗吸收 - 大于阈值不带某光环", CountKind.UnitsWithoutAuraAboveHealingAbsorb),
|
||
new("治疗吸收 - 大于阈值带某光环", CountKind.UnitsWithAuraAboveHealingAbsorb),
|
||
new("光环 - 带某光环", CountKind.UnitsWithAura)
|
||
];
|
||
|
||
private static readonly ThresholdModeItem[] ThresholdModeOptions =
|
||
[
|
||
new("固定阈值", false),
|
||
new("动态阈值", true)
|
||
];
|
||
|
||
private readonly IReadOnlyList<string> _auraFields;
|
||
private readonly IReadOnlyList<string> _thresholdFields;
|
||
private readonly HashSet<string> _takenNames;
|
||
|
||
private readonly Label _healthNameLabel = new();
|
||
private readonly TextBox _nameBox = new();
|
||
private readonly TextBox _healthNameBox = new();
|
||
private readonly ComboBox _categoryBox = new();
|
||
private readonly ComboBox _selectorBox = new();
|
||
private readonly ComboBox _lowestHealthAuraFilterBox = new();
|
||
private readonly ComboBox _lowestHealthRoleFilterBox = new();
|
||
private readonly FlowLayoutPanel _paramPanel = new();
|
||
private readonly Label _previewLabel = new();
|
||
private readonly ToolTip _toolTip = new();
|
||
|
||
private readonly NumericUpDown _thresholdBox = new();
|
||
private readonly ComboBox _thresholdModeBox = new();
|
||
private readonly ComboBox _thresholdFieldBox = new();
|
||
private readonly ComboBox _roleBox = new();
|
||
private readonly CheckBox _reverseBox = new();
|
||
private readonly ComboBox _auraBox = new();
|
||
private readonly CheckedListBox _aurasBox = new();
|
||
private readonly NumericUpDown _auraCountBox = new();
|
||
private readonly ComboBox _dispelTypeBox = new();
|
||
|
||
private Panel _thresholdModeRow = null!;
|
||
private Panel _thresholdRow = null!;
|
||
private Label _thresholdLabel = null!;
|
||
private Panel _thresholdFieldRow = null!;
|
||
private Panel _lowestHealthAuraFilterRow = null!;
|
||
private Panel _lowestHealthRoleFilterRow = null!;
|
||
private Panel _roleRow = null!;
|
||
private Panel _reverseRow = null!;
|
||
private Panel _auraRow = null!;
|
||
private Panel _aurasRow = null!;
|
||
private Panel _auraCountRow = null!;
|
||
private Panel _dispelRow = null!;
|
||
private bool _usesHealingAbsorbThreshold;
|
||
|
||
public ModuleUnit? ResultUnit { get; private set; }
|
||
public ModuleCountField? ResultCount { get; private set; }
|
||
|
||
public UnitEditorForm(
|
||
IReadOnlyList<string> auraFields,
|
||
IReadOnlyList<string> thresholdFields,
|
||
IReadOnlyCollection<string> takenNames,
|
||
ModuleUnit? existingUnit,
|
||
ModuleCountField? existingCount)
|
||
{
|
||
_auraFields = auraFields;
|
||
_thresholdFields = thresholdFields;
|
||
_takenNames = new HashSet<string>(takenNames, StringComparer.OrdinalIgnoreCase);
|
||
InitializeComponent();
|
||
Seed(existingUnit, existingCount);
|
||
UpdateParamVisibility();
|
||
UpdateHealthNameState();
|
||
}
|
||
|
||
protected override void OnHandleCreated(EventArgs e)
|
||
{
|
||
base.OnHandleCreated(e);
|
||
UiTheme.ApplyDarkTitleBar(this);
|
||
}
|
||
|
||
protected override void OnShown(EventArgs e)
|
||
{
|
||
base.OnShown(e);
|
||
_nameBox.Focus();
|
||
_nameBox.SelectAll();
|
||
}
|
||
|
||
private void InitializeComponent()
|
||
{
|
||
Text = "编辑单位";
|
||
Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point);
|
||
AutoScaleMode = AutoScaleMode.Dpi;
|
||
BackColor = UiTheme.Surface;
|
||
ForeColor = UiTheme.Text;
|
||
ClientSize = new Size(RowWidth + 36, 534);
|
||
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||
MaximizeBox = false;
|
||
MinimizeBox = false;
|
||
ShowInTaskbar = false;
|
||
StartPosition = FormStartPosition.CenterParent;
|
||
|
||
var root = new TableLayoutPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
BackColor = UiTheme.Surface,
|
||
Padding = new Padding(UiTheme.CardPadding, 12, UiTheme.CardPadding, 12),
|
||
ColumnCount = 1,
|
||
RowCount = 4
|
||
};
|
||
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 112));
|
||
root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
|
||
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 48));
|
||
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 56));
|
||
Controls.Add(root);
|
||
|
||
UiTheme.StyleComboBox(_categoryBox);
|
||
_categoryBox.DropDownWidth = 180;
|
||
_categoryBox.Items.AddRange(["单位 (可作目标)", "数量 (仅条件)"]);
|
||
_categoryBox.SelectedIndex = 0;
|
||
_categoryBox.SelectedIndexChanged += (_, _) =>
|
||
{
|
||
PopulateSelectors();
|
||
UpdateParamVisibility();
|
||
UpdateHealthNameState();
|
||
};
|
||
|
||
UiTheme.StyleComboBox(_selectorBox);
|
||
_selectorBox.DropDownWidth = 360;
|
||
_selectorBox.SelectedIndexChanged += (_, _) =>
|
||
{
|
||
UpdateParamVisibility();
|
||
UpdateHealthNameState();
|
||
};
|
||
|
||
var headerCard = new UiCardPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
Padding = new Padding(4),
|
||
Margin = new Padding(0, 0, 0, UiTheme.PageGap),
|
||
ColumnCount = 1,
|
||
RowCount = 2
|
||
};
|
||
headerCard.RowStyles.Add(new RowStyle(SizeType.Percent, 50));
|
||
headerCard.RowStyles.Add(new RowStyle(SizeType.Percent, 50));
|
||
headerCard.Controls.Add(BuildSplitRow("类别", _categoryBox, "选择器", _selectorBox), 0, 0);
|
||
headerCard.Controls.Add(BuildNameRow(), 0, 1);
|
||
root.Controls.Add(headerCard, 0, 0);
|
||
|
||
_paramPanel.Dock = DockStyle.Fill;
|
||
_paramPanel.BackColor = Color.Transparent;
|
||
_paramPanel.FlowDirection = FlowDirection.TopDown;
|
||
_paramPanel.WrapContents = false;
|
||
_paramPanel.AutoScroll = true;
|
||
_paramPanel.Margin = new Padding(0);
|
||
_paramPanel.Padding = new Padding(4, 6, 4, 6);
|
||
BuildParamRows();
|
||
var paramsCard = new UiCardPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
Padding = new Padding(4),
|
||
Margin = new Padding(0, 0, 0, UiTheme.PageGap),
|
||
ColumnCount = 1,
|
||
RowCount = 1
|
||
};
|
||
paramsCard.Controls.Add(_paramPanel, 0, 0);
|
||
root.Controls.Add(paramsCard, 0, 1);
|
||
|
||
_previewLabel.Dock = DockStyle.Fill;
|
||
_previewLabel.ForeColor = UiTheme.Muted;
|
||
_previewLabel.TextAlign = ContentAlignment.MiddleLeft;
|
||
_previewLabel.AutoEllipsis = true;
|
||
_previewLabel.Margin = new Padding(0);
|
||
var previewCard = new UiCardPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
Padding = new Padding(UiTheme.CardPadding, 6, UiTheme.CardPadding, 6),
|
||
Margin = new Padding(0, 0, 0, UiTheme.PageGap),
|
||
ColumnCount = 1,
|
||
RowCount = 1
|
||
};
|
||
previewCard.Controls.Add(_previewLabel, 0, 0);
|
||
root.Controls.Add(previewCard, 0, 2);
|
||
|
||
root.Controls.Add(BuildActionRow(), 0, 3);
|
||
|
||
PopulateSelectors();
|
||
}
|
||
|
||
private void BuildParamRows()
|
||
{
|
||
_thresholdBox.Minimum = 1;
|
||
_thresholdBox.Maximum = int.MaxValue;
|
||
_thresholdBox.Value = 100;
|
||
UiTheme.StyleNumericUpDown(_thresholdBox);
|
||
|
||
UiTheme.StyleComboBox(_thresholdModeBox);
|
||
_thresholdModeBox.DropDownWidth = 160;
|
||
_thresholdModeBox.Items.AddRange(ThresholdModeOptions.Cast<object>().ToArray());
|
||
_thresholdModeBox.SelectedIndex = 0;
|
||
_thresholdModeBox.SelectedIndexChanged += (_, _) => UpdateParamVisibility();
|
||
|
||
UiTheme.StyleComboBox(_thresholdFieldBox);
|
||
_thresholdFieldBox.DropDownWidth = 360;
|
||
foreach (var field in _thresholdFields)
|
||
{
|
||
if (!_thresholdFieldBox.Items.Contains(field))
|
||
{
|
||
_thresholdFieldBox.Items.Add(field);
|
||
}
|
||
}
|
||
|
||
if (_thresholdFieldBox.Items.Count > 0)
|
||
{
|
||
_thresholdFieldBox.SelectedIndex = 0;
|
||
}
|
||
|
||
UiTheme.StyleComboBox(_lowestHealthAuraFilterBox);
|
||
_lowestHealthAuraFilterBox.DropDownWidth = 220;
|
||
_lowestHealthAuraFilterBox.Items.AddRange(LowestHealthAuraFilterOptions.Cast<object>().ToArray());
|
||
_lowestHealthAuraFilterBox.SelectedIndex = 0;
|
||
_lowestHealthAuraFilterBox.SelectedIndexChanged += (_, _) => UpdateParamVisibility();
|
||
|
||
UiTheme.StyleComboBox(_lowestHealthRoleFilterBox);
|
||
_lowestHealthRoleFilterBox.DropDownWidth = 220;
|
||
_lowestHealthRoleFilterBox.Items.AddRange(LowestHealthRoleFilterOptions.Cast<object>().ToArray());
|
||
_lowestHealthRoleFilterBox.SelectedIndex = 0;
|
||
_lowestHealthRoleFilterBox.SelectedIndexChanged += (_, _) => UpdateParamVisibility();
|
||
|
||
UiTheme.StyleComboBox(_roleBox);
|
||
_roleBox.DropDownWidth = 160;
|
||
_roleBox.Items.AddRange(RoleOptions.Cast<object>().ToArray());
|
||
_roleBox.SelectedIndex = 0;
|
||
|
||
_reverseBox.Text = "取逆序最后一个匹配单位";
|
||
UiTheme.StyleCheckBox(_reverseBox, UiTheme.SurfaceRaised);
|
||
_reverseBox.AutoSize = false;
|
||
_reverseBox.TextAlign = ContentAlignment.MiddleLeft;
|
||
|
||
UiTheme.StyleComboBox(_auraBox);
|
||
_auraBox.DropDownWidth = 360;
|
||
foreach (var aura in _auraFields)
|
||
{
|
||
_auraBox.Items.Add(aura);
|
||
}
|
||
|
||
if (_auraBox.Items.Count > 0)
|
||
{
|
||
_auraBox.SelectedIndex = 0;
|
||
}
|
||
|
||
UiTheme.StyleCheckedListBox(_aurasBox);
|
||
foreach (var aura in _auraFields)
|
||
{
|
||
_aurasBox.Items.Add(aura);
|
||
}
|
||
|
||
_auraCountBox.Minimum = 0;
|
||
_auraCountBox.Maximum = 100;
|
||
_auraCountBox.Value = 1;
|
||
UiTheme.StyleNumericUpDown(_auraCountBox);
|
||
|
||
UiTheme.StyleComboBox(_dispelTypeBox);
|
||
_dispelTypeBox.DropDownWidth = 180;
|
||
_dispelTypeBox.Items.AddRange(DispelTypeOptions.Cast<object>().ToArray());
|
||
_dispelTypeBox.SelectedIndex = 0;
|
||
|
||
// 参数值变化刷新底部实时预览(类别/选择器/阈值类型/光环筛选经 UpdateParamVisibility 间接刷新)。
|
||
_thresholdBox.ValueChanged += (_, _) => UpdatePreview();
|
||
_thresholdFieldBox.SelectedIndexChanged += (_, _) => UpdatePreview();
|
||
_roleBox.SelectedIndexChanged += (_, _) => UpdatePreview();
|
||
_reverseBox.CheckedChanged += (_, _) => UpdatePreview();
|
||
_auraBox.SelectedIndexChanged += (_, _) => UpdatePreview();
|
||
_auraCountBox.ValueChanged += (_, _) => UpdatePreview();
|
||
_dispelTypeBox.SelectedIndexChanged += (_, _) => UpdatePreview();
|
||
// ItemCheck 在勾选状态提交前触发, 延后到提交后再读 CheckedItems。
|
||
// Seed 期间 CheckAuras 也会触发本事件, 此时窗口句柄尚未创建, 跳过(构造末尾会统一刷新)。
|
||
_aurasBox.ItemCheck += (_, _) =>
|
||
{
|
||
if (IsHandleCreated)
|
||
{
|
||
BeginInvoke(new Action(UpdatePreview));
|
||
}
|
||
};
|
||
|
||
_thresholdModeRow = BuildLabeledRow("阈值类型", _thresholdModeBox);
|
||
_thresholdRow = BuildLabeledRow("血量阈值 (<)", _thresholdBox);
|
||
_thresholdLabel = _thresholdRow.Controls.OfType<Label>().Single();
|
||
_thresholdFieldRow = BuildLabeledRow("动态阈值", _thresholdFieldBox);
|
||
_lowestHealthAuraFilterRow = BuildLabeledRow("光环筛选", _lowestHealthAuraFilterBox);
|
||
_lowestHealthRoleFilterRow = BuildLabeledRow("职责筛选", _lowestHealthRoleFilterBox);
|
||
_roleRow = BuildLabeledRow("职责", _roleBox);
|
||
_reverseRow = BuildLabeledRow("顺序", _reverseBox);
|
||
_auraRow = BuildLabeledRow("光环", _auraBox);
|
||
_aurasRow = BuildLabeledRow("光环 (可多选)", _aurasBox, 116);
|
||
_auraCountRow = BuildLabeledRow("光环值", _auraCountBox);
|
||
_dispelRow = BuildLabeledRow("驱散类型", _dispelTypeBox);
|
||
|
||
_paramPanel.Controls.AddRange([_thresholdModeRow, _thresholdRow, _thresholdFieldRow, _lowestHealthAuraFilterRow, _lowestHealthRoleFilterRow, _roleRow, _reverseRow, _auraRow, _aurasRow, _auraCountRow, _dispelRow]);
|
||
}
|
||
|
||
private Control BuildActionRow()
|
||
{
|
||
var row = new UiCardPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
Padding = new Padding(UiTheme.CardPadding, 10, UiTheme.CardPadding, 10),
|
||
Margin = new Padding(0),
|
||
ColumnCount = 2,
|
||
RowCount = 1
|
||
};
|
||
row.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
|
||
row.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 184));
|
||
row.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
|
||
|
||
var actions = new FlowLayoutPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
FlowDirection = FlowDirection.RightToLeft,
|
||
WrapContents = false,
|
||
BackColor = Color.Transparent,
|
||
Margin = new Padding(0)
|
||
};
|
||
|
||
var okButton = UiTheme.CreateButton("确定", UiTheme.ButtonKind.Primary);
|
||
UiTheme.StyleActionButton(okButton, 84);
|
||
okButton.Margin = new Padding(8, 0, 0, 0);
|
||
okButton.Click += (_, _) => OnConfirm();
|
||
|
||
var cancelButton = UiTheme.CreateButton("取消", UiTheme.ButtonKind.Secondary);
|
||
UiTheme.StyleActionButton(cancelButton, 84);
|
||
cancelButton.Margin = new Padding(8, 0, 0, 0);
|
||
cancelButton.Click += (_, _) => DialogResult = DialogResult.Cancel;
|
||
|
||
actions.Controls.Add(okButton);
|
||
actions.Controls.Add(cancelButton);
|
||
row.Controls.Add(actions, 1, 0);
|
||
AcceptButton = okButton;
|
||
CancelButton = cancelButton;
|
||
return row;
|
||
}
|
||
|
||
private void PopulateSelectors()
|
||
{
|
||
_selectorBox.Items.Clear();
|
||
if (IsCountCategory)
|
||
{
|
||
_selectorBox.Items.AddRange(CountSelectors.Cast<object>().ToArray());
|
||
}
|
||
else
|
||
{
|
||
_selectorBox.Items.AddRange(UnitSelectors.Cast<object>().ToArray());
|
||
}
|
||
|
||
if (_selectorBox.Items.Count > 0)
|
||
{
|
||
_selectorBox.SelectedIndex = 0;
|
||
}
|
||
}
|
||
|
||
// 值名称只对"生命值最低"单位有意义(把该单位的 生命值 暴露成数值条件字段)。
|
||
private void UpdateHealthNameState()
|
||
{
|
||
var visible = SupportsHealthName();
|
||
_healthNameLabel.Visible = visible;
|
||
_healthNameBox.Visible = visible;
|
||
_healthNameBox.Enabled = visible;
|
||
if (!visible)
|
||
{
|
||
_healthNameBox.Text = string.Empty;
|
||
}
|
||
}
|
||
|
||
private void UpdateParamVisibility()
|
||
{
|
||
bool threshold = false, lowestHealthAuraFilter = false, lowestHealthRoleFilter = false, role = false, reverse = false, auraSingle = false, auraMulti = false, auraCount = false, dispel = false;
|
||
|
||
if (IsCountCategory)
|
||
{
|
||
switch ((_selectorBox.SelectedItem as CountItem)?.Kind)
|
||
{
|
||
case CountKind.UnitsBelowHealth:
|
||
threshold = true;
|
||
break;
|
||
case CountKind.UnitsWithoutAuraBelowHealth:
|
||
threshold = auraSingle = true;
|
||
break;
|
||
case CountKind.UnitsWithAuraBelowHealth:
|
||
threshold = auraSingle = true;
|
||
break;
|
||
case CountKind.UnitsWithAura:
|
||
auraSingle = true;
|
||
break;
|
||
case CountKind.UnitsAboveHealingAbsorb:
|
||
threshold = true;
|
||
break;
|
||
case CountKind.UnitsWithoutAuraAboveHealingAbsorb:
|
||
case CountKind.UnitsWithAuraAboveHealingAbsorb:
|
||
threshold = auraSingle = true;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
switch ((_selectorBox.SelectedItem as SelectorItem)?.Kind)
|
||
{
|
||
case UnitSelectorKind.LowestHealth:
|
||
threshold = lowestHealthAuraFilter = true;
|
||
lowestHealthRoleFilter = true;
|
||
role = SelectedLowestHealthRoleFilter() is not null;
|
||
switch (SelectedLowestHealthAuraFilter())
|
||
{
|
||
case LowestHealthAuraFilterKind.WithAnyAura:
|
||
case LowestHealthAuraFilterKind.WithoutAnyAura:
|
||
auraMulti = true;
|
||
break;
|
||
case LowestHealthAuraFilterKind.WithoutAura:
|
||
case LowestHealthAuraFilterKind.WithAura:
|
||
auraSingle = true;
|
||
break;
|
||
case LowestHealthAuraFilterKind.WithAuraCount:
|
||
auraSingle = auraCount = true;
|
||
break;
|
||
}
|
||
|
||
break;
|
||
case UnitSelectorKind.HighestHealingAbsorb:
|
||
threshold = lowestHealthAuraFilter = true;
|
||
switch (SelectedLowestHealthAuraFilter())
|
||
{
|
||
case LowestHealthAuraFilterKind.WithAnyAura:
|
||
case LowestHealthAuraFilterKind.WithoutAnyAura:
|
||
auraMulti = true;
|
||
break;
|
||
case LowestHealthAuraFilterKind.WithoutAura:
|
||
case LowestHealthAuraFilterKind.WithAura:
|
||
auraSingle = true;
|
||
break;
|
||
case LowestHealthAuraFilterKind.WithAuraCount:
|
||
auraSingle = auraCount = true;
|
||
break;
|
||
}
|
||
|
||
break;
|
||
case UnitSelectorKind.LowestHealthWithAnyAura:
|
||
case UnitSelectorKind.LowestHealthWithoutAnyAura:
|
||
threshold = auraMulti = true;
|
||
break;
|
||
case UnitSelectorKind.LowestHealthWithoutAura:
|
||
case UnitSelectorKind.LowestHealthWithAura:
|
||
threshold = auraSingle = true;
|
||
break;
|
||
case UnitSelectorKind.LowestHealthWithAuraCount:
|
||
case UnitSelectorKind.HighestHealingAbsorbWithAuraCount:
|
||
threshold = auraSingle = auraCount = true;
|
||
break;
|
||
case UnitSelectorKind.HighestHealingAbsorbWithAnyAura:
|
||
case UnitSelectorKind.HighestHealingAbsorbWithoutAnyAura:
|
||
threshold = auraMulti = true;
|
||
break;
|
||
case UnitSelectorKind.HighestHealingAbsorbWithoutAura:
|
||
case UnitSelectorKind.HighestHealingAbsorbWithAura:
|
||
threshold = auraSingle = true;
|
||
break;
|
||
case UnitSelectorKind.UnitWithRole:
|
||
role = reverse = true;
|
||
break;
|
||
case UnitSelectorKind.UnitWithRoleWithoutAura:
|
||
role = reverse = auraSingle = true;
|
||
break;
|
||
case UnitSelectorKind.UnitWithAura:
|
||
auraSingle = true;
|
||
break;
|
||
case UnitSelectorKind.UnitWithDispelType:
|
||
dispel = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
var dynamicThreshold = IsDynamicThresholdMode();
|
||
UpdateThresholdPresentation();
|
||
_thresholdModeRow.Visible = threshold;
|
||
_thresholdRow.Visible = threshold && !dynamicThreshold;
|
||
_thresholdFieldRow.Visible = threshold && dynamicThreshold;
|
||
_lowestHealthAuraFilterRow.Visible = lowestHealthAuraFilter;
|
||
_lowestHealthRoleFilterRow.Visible = lowestHealthRoleFilter;
|
||
_roleRow.Visible = role;
|
||
_reverseRow.Visible = reverse;
|
||
_auraRow.Visible = auraSingle;
|
||
_aurasRow.Visible = auraMulti;
|
||
_auraCountRow.Visible = auraCount;
|
||
_dispelRow.Visible = dispel;
|
||
UpdatePreview();
|
||
}
|
||
|
||
private void UpdatePreview()
|
||
{
|
||
var text = BuildPreviewText();
|
||
_previewLabel.Text = string.IsNullOrEmpty(text) ? "预览: -" : $"预览: {text}";
|
||
}
|
||
|
||
// 用当前控件状态构造一个宽容的(不校验、不弹框)单位/数量, 复用 UnitSummary 渲染预览。
|
||
private string BuildPreviewText()
|
||
{
|
||
if (IsCountCategory)
|
||
{
|
||
var count = new ModuleCountField
|
||
{
|
||
Name = _nameBox.Text.Trim(),
|
||
Kind = (_selectorBox.SelectedItem as CountItem)?.Kind ?? CountKind.UnitsBelowHealth,
|
||
AuraName = SelectedAura()
|
||
};
|
||
ApplyPreviewThreshold(v => count.HealthThreshold = v, f => count.HealthThresholdField = f);
|
||
return UnitSummary.Describe(count);
|
||
}
|
||
|
||
var unit = new ModuleUnit
|
||
{
|
||
Name = _nameBox.Text.Trim(),
|
||
Kind = ResolveSelectedUnitKind(),
|
||
RoleFilter = SelectedLowestHealthRoleFilter(),
|
||
Role = SelectedRole(),
|
||
Reverse = _reverseBox.Checked,
|
||
AuraCount = (int)_auraCountBox.Value,
|
||
DispelType = SelectedDispelType()
|
||
};
|
||
unit.AuraNames = unit.Kind is UnitSelectorKind.LowestHealthWithAnyAura
|
||
or UnitSelectorKind.LowestHealthWithoutAnyAura
|
||
or UnitSelectorKind.HighestHealingAbsorbWithAnyAura
|
||
or UnitSelectorKind.HighestHealingAbsorbWithoutAnyAura
|
||
? CheckedAuras()
|
||
: SingleAuraList();
|
||
ApplyPreviewThreshold(v => unit.HealthThreshold = v, f => unit.HealthThresholdField = f);
|
||
return UnitSummary.Describe(unit);
|
||
}
|
||
|
||
// "生命值最低"/"治疗吸收最高" 的具体子类型取决于光环筛选下拉, 与 OnConfirm 的分支保持一致。
|
||
private UnitSelectorKind ResolveSelectedUnitKind()
|
||
{
|
||
var kind = (_selectorBox.SelectedItem as SelectorItem)?.Kind ?? UnitSelectorKind.LowestHealth;
|
||
if (kind is not UnitSelectorKind.LowestHealth and not UnitSelectorKind.HighestHealingAbsorb)
|
||
{
|
||
return kind;
|
||
}
|
||
|
||
var healingAbsorb = kind == UnitSelectorKind.HighestHealingAbsorb;
|
||
return SelectedLowestHealthAuraFilter() switch
|
||
{
|
||
LowestHealthAuraFilterKind.WithAnyAura => healingAbsorb
|
||
? UnitSelectorKind.HighestHealingAbsorbWithAnyAura
|
||
: UnitSelectorKind.LowestHealthWithAnyAura,
|
||
LowestHealthAuraFilterKind.WithoutAnyAura => healingAbsorb
|
||
? UnitSelectorKind.HighestHealingAbsorbWithoutAnyAura
|
||
: UnitSelectorKind.LowestHealthWithoutAnyAura,
|
||
LowestHealthAuraFilterKind.WithoutAura => healingAbsorb
|
||
? UnitSelectorKind.HighestHealingAbsorbWithoutAura
|
||
: UnitSelectorKind.LowestHealthWithoutAura,
|
||
LowestHealthAuraFilterKind.WithAura => healingAbsorb
|
||
? UnitSelectorKind.HighestHealingAbsorbWithAura
|
||
: UnitSelectorKind.LowestHealthWithAura,
|
||
LowestHealthAuraFilterKind.WithAuraCount => healingAbsorb
|
||
? UnitSelectorKind.HighestHealingAbsorbWithAuraCount
|
||
: UnitSelectorKind.LowestHealthWithAuraCount,
|
||
_ => kind
|
||
};
|
||
}
|
||
|
||
private void ApplyPreviewThreshold(Action<int?> setFixed, Action<string?> setField)
|
||
{
|
||
if (IsDynamicThresholdMode())
|
||
{
|
||
setField(_thresholdFieldBox.SelectedItem?.ToString()?.Trim());
|
||
}
|
||
else
|
||
{
|
||
setFixed((int)_thresholdBox.Value);
|
||
}
|
||
}
|
||
|
||
private void Seed(ModuleUnit? unit, ModuleCountField? count)
|
||
{
|
||
if (count is not null)
|
||
{
|
||
_nameBox.Text = count.Name;
|
||
_categoryBox.SelectedIndex = 1;
|
||
PopulateSelectors();
|
||
SelectSelector(count.Kind);
|
||
if (count.HealthThreshold is { } th)
|
||
{
|
||
_thresholdBox.Value = Clamp(th, _thresholdBox);
|
||
}
|
||
|
||
SeedThresholdField(count.HealthThresholdField);
|
||
SelectAura(_auraBox, count.AuraName);
|
||
return;
|
||
}
|
||
|
||
if (unit is not null)
|
||
{
|
||
_nameBox.Text = unit.Name;
|
||
_healthNameBox.Text = unit.HealthName ?? string.Empty;
|
||
_categoryBox.SelectedIndex = 0;
|
||
PopulateSelectors();
|
||
SelectSelector(DisplaySelectorKind(unit.Kind));
|
||
SelectLowestHealthAuraFilter(unit.Kind);
|
||
SelectLowestHealthRoleFilter(unit.RoleFilter);
|
||
if (unit.HealthThreshold is { } th)
|
||
{
|
||
_thresholdBox.Value = Clamp(th, _thresholdBox);
|
||
}
|
||
|
||
SeedThresholdField(unit.HealthThresholdField);
|
||
if (unit.Role is { } r)
|
||
{
|
||
SelectRole(r);
|
||
}
|
||
|
||
_reverseBox.Checked = unit.Reverse;
|
||
SelectAura(_auraBox, unit.AuraNames is { Count: > 0 } ? unit.AuraNames[0] : null);
|
||
CheckAuras(unit.AuraNames);
|
||
if (unit.AuraCount is { } ac)
|
||
{
|
||
_auraCountBox.Value = Clamp(ac, _auraCountBox);
|
||
}
|
||
|
||
if (unit.DispelType is { } dt)
|
||
{
|
||
SelectDispelType(dt);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void OnConfirm()
|
||
{
|
||
var name = _nameBox.Text.Trim();
|
||
if (!ValidateName(name, out var message))
|
||
{
|
||
MessageBox.Show(message, "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
if (IsCountCategory)
|
||
{
|
||
var kind = (_selectorBox.SelectedItem as CountItem)?.Kind ?? CountKind.UnitsBelowHealth;
|
||
var count = new ModuleCountField { Name = name, Kind = kind };
|
||
switch (kind)
|
||
{
|
||
case CountKind.UnitsBelowHealth:
|
||
if (!ApplyThreshold(count))
|
||
{
|
||
return;
|
||
}
|
||
|
||
break;
|
||
case CountKind.UnitsWithoutAuraBelowHealth:
|
||
if (!ApplyThreshold(count))
|
||
{
|
||
return;
|
||
}
|
||
|
||
count.AuraName = SelectedAura();
|
||
break;
|
||
case CountKind.UnitsWithAuraBelowHealth:
|
||
if (!ApplyThreshold(count))
|
||
{
|
||
return;
|
||
}
|
||
|
||
count.AuraName = SelectedAura();
|
||
break;
|
||
case CountKind.UnitsWithAura:
|
||
count.AuraName = SelectedAura();
|
||
break;
|
||
case CountKind.UnitsAboveHealingAbsorb:
|
||
if (!ApplyThreshold(count))
|
||
{
|
||
return;
|
||
}
|
||
|
||
break;
|
||
case CountKind.UnitsWithoutAuraAboveHealingAbsorb:
|
||
case CountKind.UnitsWithAuraAboveHealingAbsorb:
|
||
if (!ApplyThreshold(count))
|
||
{
|
||
return;
|
||
}
|
||
|
||
count.AuraName = SelectedAura();
|
||
break;
|
||
}
|
||
|
||
if (RequiresAura(kind) && string.IsNullOrEmpty(count.AuraName))
|
||
{
|
||
MessageBox.Show("请选择光环。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
ResultCount = count;
|
||
DialogResult = DialogResult.OK;
|
||
return;
|
||
}
|
||
|
||
var selectorKind = (_selectorBox.SelectedItem as SelectorItem)?.Kind ?? UnitSelectorKind.LowestHealth;
|
||
var moduleUnit = new ModuleUnit { Name = name, Kind = selectorKind };
|
||
switch (selectorKind)
|
||
{
|
||
case UnitSelectorKind.LowestHealth:
|
||
case UnitSelectorKind.HighestHealingAbsorb:
|
||
if (!ApplyThreshold(moduleUnit))
|
||
{
|
||
return;
|
||
}
|
||
|
||
var healingAbsorb = selectorKind == UnitSelectorKind.HighestHealingAbsorb;
|
||
if (!healingAbsorb && SelectedLowestHealthRoleFilter() is { } roleFilter)
|
||
{
|
||
moduleUnit.RoleFilter = roleFilter;
|
||
moduleUnit.Role = SelectedRole();
|
||
}
|
||
|
||
switch (SelectedLowestHealthAuraFilter())
|
||
{
|
||
case LowestHealthAuraFilterKind.WithAnyAura:
|
||
moduleUnit.Kind = healingAbsorb
|
||
? UnitSelectorKind.HighestHealingAbsorbWithAnyAura
|
||
: UnitSelectorKind.LowestHealthWithAnyAura;
|
||
moduleUnit.AuraNames = CheckedAuras();
|
||
if (moduleUnit.AuraNames.Count == 0)
|
||
{
|
||
MessageBox.Show("请至少勾选一个光环。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
break;
|
||
case LowestHealthAuraFilterKind.WithoutAnyAura:
|
||
moduleUnit.Kind = healingAbsorb
|
||
? UnitSelectorKind.HighestHealingAbsorbWithoutAnyAura
|
||
: UnitSelectorKind.LowestHealthWithoutAnyAura;
|
||
moduleUnit.AuraNames = CheckedAuras();
|
||
if (moduleUnit.AuraNames.Count == 0)
|
||
{
|
||
MessageBox.Show("请至少勾选一个光环。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
break;
|
||
case LowestHealthAuraFilterKind.WithoutAura:
|
||
moduleUnit.Kind = healingAbsorb
|
||
? UnitSelectorKind.HighestHealingAbsorbWithoutAura
|
||
: UnitSelectorKind.LowestHealthWithoutAura;
|
||
moduleUnit.AuraNames = SingleAuraList();
|
||
break;
|
||
case LowestHealthAuraFilterKind.WithAura:
|
||
moduleUnit.Kind = healingAbsorb
|
||
? UnitSelectorKind.HighestHealingAbsorbWithAura
|
||
: UnitSelectorKind.LowestHealthWithAura;
|
||
moduleUnit.AuraNames = SingleAuraList();
|
||
break;
|
||
case LowestHealthAuraFilterKind.WithAuraCount:
|
||
moduleUnit.Kind = healingAbsorb
|
||
? UnitSelectorKind.HighestHealingAbsorbWithAuraCount
|
||
: UnitSelectorKind.LowestHealthWithAuraCount;
|
||
moduleUnit.AuraNames = SingleAuraList();
|
||
moduleUnit.AuraCount = (int)_auraCountBox.Value;
|
||
break;
|
||
}
|
||
|
||
break;
|
||
case UnitSelectorKind.LowestHealthWithAnyAura:
|
||
case UnitSelectorKind.LowestHealthWithoutAnyAura:
|
||
case UnitSelectorKind.HighestHealingAbsorbWithAnyAura:
|
||
case UnitSelectorKind.HighestHealingAbsorbWithoutAnyAura:
|
||
if (!ApplyThreshold(moduleUnit))
|
||
{
|
||
return;
|
||
}
|
||
|
||
moduleUnit.AuraNames = CheckedAuras();
|
||
if (moduleUnit.AuraNames.Count == 0)
|
||
{
|
||
MessageBox.Show("请至少勾选一个光环。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
break;
|
||
case UnitSelectorKind.LowestHealthWithoutAura:
|
||
case UnitSelectorKind.LowestHealthWithAura:
|
||
case UnitSelectorKind.HighestHealingAbsorbWithoutAura:
|
||
case UnitSelectorKind.HighestHealingAbsorbWithAura:
|
||
if (!ApplyThreshold(moduleUnit))
|
||
{
|
||
return;
|
||
}
|
||
|
||
moduleUnit.AuraNames = SingleAuraList();
|
||
break;
|
||
case UnitSelectorKind.LowestHealthWithAuraCount:
|
||
case UnitSelectorKind.HighestHealingAbsorbWithAuraCount:
|
||
if (!ApplyThreshold(moduleUnit))
|
||
{
|
||
return;
|
||
}
|
||
|
||
moduleUnit.AuraNames = SingleAuraList();
|
||
moduleUnit.AuraCount = (int)_auraCountBox.Value;
|
||
break;
|
||
case UnitSelectorKind.UnitWithRole:
|
||
moduleUnit.Role = SelectedRole();
|
||
moduleUnit.Reverse = _reverseBox.Checked;
|
||
break;
|
||
case UnitSelectorKind.UnitWithRoleWithoutAura:
|
||
moduleUnit.Role = SelectedRole();
|
||
moduleUnit.Reverse = _reverseBox.Checked;
|
||
moduleUnit.AuraNames = SingleAuraList();
|
||
break;
|
||
case UnitSelectorKind.UnitWithAura:
|
||
moduleUnit.AuraNames = SingleAuraList();
|
||
break;
|
||
case UnitSelectorKind.UnitWithDispelType:
|
||
moduleUnit.DispelType = SelectedDispelType();
|
||
break;
|
||
}
|
||
|
||
if (UnitRequiresAura(moduleUnit.Kind) && (moduleUnit.AuraNames is null || moduleUnit.AuraNames.Count == 0))
|
||
{
|
||
MessageBox.Show("请选择光环。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
var healthName = SupportsHealthName() ? _healthNameBox.Text.Trim() : string.Empty;
|
||
if (healthName.Length > 0)
|
||
{
|
||
if (string.Equals(healthName, name, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
MessageBox.Show("值名称不能与名称相同。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
if (!ValidateName(healthName, out var healthMessage))
|
||
{
|
||
MessageBox.Show($"值名称: {healthMessage}", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
}
|
||
|
||
moduleUnit.HealthName = healthName.Length == 0 ? null : healthName;
|
||
ResultUnit = moduleUnit;
|
||
DialogResult = DialogResult.OK;
|
||
}
|
||
|
||
private bool ValidateName(string name, out string message)
|
||
{
|
||
message = string.Empty;
|
||
if (name.Length == 0)
|
||
{
|
||
message = "名称不能为空。";
|
||
return false;
|
||
}
|
||
|
||
if (name.Contains('.') || name.Contains('$'))
|
||
{
|
||
message = "名称不能包含 '.' 或 '$'。";
|
||
return false;
|
||
}
|
||
|
||
if (int.TryParse(name, out _))
|
||
{
|
||
message = "名称不能是纯数字(会与单位编号混淆)。";
|
||
return false;
|
||
}
|
||
|
||
if (_takenNames.Contains(name))
|
||
{
|
||
message = $"名称“{name}”已被其它单位/字段或状态字段占用。";
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private bool IsCountCategory => _categoryBox.SelectedIndex == 1;
|
||
|
||
private bool IsDynamicThresholdMode()
|
||
=> (_thresholdModeBox.SelectedItem as ThresholdModeItem)?.UsesDynamicField == true;
|
||
|
||
private bool IsHealingAbsorbSelector()
|
||
{
|
||
if (IsCountCategory)
|
||
{
|
||
return (_selectorBox.SelectedItem as CountItem)?.Kind is
|
||
CountKind.UnitsAboveHealingAbsorb
|
||
or CountKind.UnitsWithoutAuraAboveHealingAbsorb
|
||
or CountKind.UnitsWithAuraAboveHealingAbsorb;
|
||
}
|
||
|
||
return (_selectorBox.SelectedItem as SelectorItem)?.Kind == UnitSelectorKind.HighestHealingAbsorb;
|
||
}
|
||
|
||
private void UpdateThresholdPresentation()
|
||
{
|
||
var usesHealingAbsorb = IsHealingAbsorbSelector();
|
||
if (usesHealingAbsorb != _usesHealingAbsorbThreshold)
|
||
{
|
||
if (usesHealingAbsorb)
|
||
{
|
||
_thresholdBox.Minimum = 0;
|
||
_thresholdBox.Value = 0;
|
||
}
|
||
else
|
||
{
|
||
_thresholdBox.Value = 100;
|
||
_thresholdBox.Minimum = 1;
|
||
}
|
||
|
||
_usesHealingAbsorbThreshold = usesHealingAbsorb;
|
||
}
|
||
|
||
_thresholdLabel.Text = usesHealingAbsorb
|
||
? "治疗吸收阈值 (>)"
|
||
: "血量阈值 (<)";
|
||
}
|
||
|
||
private bool ApplyThreshold(ModuleUnit unit)
|
||
{
|
||
if (TryReadThreshold(out var fixedValue, out var field))
|
||
{
|
||
unit.HealthThreshold = fixedValue;
|
||
unit.HealthThresholdField = field;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private bool ApplyThreshold(ModuleCountField count)
|
||
{
|
||
if (TryReadThreshold(out var fixedValue, out var field))
|
||
{
|
||
count.HealthThreshold = fixedValue;
|
||
count.HealthThresholdField = field;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private bool TryReadThreshold(out int? fixedValue, out string? field)
|
||
{
|
||
if (!IsDynamicThresholdMode())
|
||
{
|
||
fixedValue = (int)_thresholdBox.Value;
|
||
field = null;
|
||
return true;
|
||
}
|
||
|
||
fixedValue = null;
|
||
field = _thresholdFieldBox.SelectedItem?.ToString()?.Trim();
|
||
if (!string.IsNullOrWhiteSpace(field))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
MessageBox.Show("请选择动态阈值。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return false;
|
||
}
|
||
|
||
private bool SupportsHealthName()
|
||
=> !IsCountCategory
|
||
&& (_selectorBox.SelectedItem as SelectorItem)?.Kind == UnitSelectorKind.LowestHealth;
|
||
|
||
private static bool RequiresAura(CountKind kind)
|
||
=> kind is CountKind.UnitsWithoutAuraBelowHealth
|
||
or CountKind.UnitsWithAuraBelowHealth
|
||
or CountKind.UnitsWithAura
|
||
or CountKind.UnitsWithoutAuraAboveHealingAbsorb
|
||
or CountKind.UnitsWithAuraAboveHealingAbsorb;
|
||
|
||
private static bool UnitRequiresAura(UnitSelectorKind kind)
|
||
=> kind is UnitSelectorKind.LowestHealthWithAnyAura
|
||
or UnitSelectorKind.LowestHealthWithoutAnyAura
|
||
or UnitSelectorKind.LowestHealthWithoutAura
|
||
or UnitSelectorKind.LowestHealthWithAura
|
||
or UnitSelectorKind.LowestHealthWithAuraCount
|
||
or UnitSelectorKind.HighestHealingAbsorbWithAnyAura
|
||
or UnitSelectorKind.HighestHealingAbsorbWithoutAnyAura
|
||
or UnitSelectorKind.HighestHealingAbsorbWithoutAura
|
||
or UnitSelectorKind.HighestHealingAbsorbWithAura
|
||
or UnitSelectorKind.HighestHealingAbsorbWithAuraCount
|
||
or UnitSelectorKind.UnitWithRoleWithoutAura
|
||
or UnitSelectorKind.UnitWithAura;
|
||
|
||
private string? SelectedAura() => _auraBox.SelectedItem?.ToString();
|
||
|
||
private LowestHealthAuraFilterKind SelectedLowestHealthAuraFilter()
|
||
=> (_lowestHealthAuraFilterBox.SelectedItem as LowestHealthAuraFilterItem)?.Kind
|
||
?? LowestHealthAuraFilterKind.None;
|
||
|
||
private UnitRoleFilterKind? SelectedLowestHealthRoleFilter()
|
||
=> (_lowestHealthRoleFilterBox.SelectedItem as LowestHealthRoleFilterItem)?.Kind;
|
||
|
||
private List<string> SingleAuraList()
|
||
{
|
||
var aura = SelectedAura();
|
||
return string.IsNullOrEmpty(aura) ? new List<string>() : new List<string> { aura };
|
||
}
|
||
|
||
private List<string> CheckedAuras()
|
||
{
|
||
var list = new List<string>();
|
||
foreach (var item in _aurasBox.CheckedItems)
|
||
{
|
||
if (item?.ToString() is { Length: > 0 } name)
|
||
{
|
||
list.Add(name);
|
||
}
|
||
}
|
||
|
||
return list;
|
||
}
|
||
|
||
private int SelectedRole() => (_roleBox.SelectedItem as RoleOption)?.Value ?? 1;
|
||
|
||
private int SelectedDispelType() => (_dispelTypeBox.SelectedItem as DispelTypeOption)?.Value ?? 1;
|
||
|
||
private static UnitSelectorKind DisplaySelectorKind(UnitSelectorKind kind)
|
||
=> kind is UnitSelectorKind.LowestHealthWithAnyAura
|
||
or UnitSelectorKind.LowestHealthWithoutAnyAura
|
||
or UnitSelectorKind.LowestHealthWithoutAura
|
||
or UnitSelectorKind.LowestHealthWithAura
|
||
or UnitSelectorKind.LowestHealthWithAuraCount
|
||
? UnitSelectorKind.LowestHealth
|
||
: kind is UnitSelectorKind.HighestHealingAbsorbWithAnyAura
|
||
or UnitSelectorKind.HighestHealingAbsorbWithoutAnyAura
|
||
or UnitSelectorKind.HighestHealingAbsorbWithoutAura
|
||
or UnitSelectorKind.HighestHealingAbsorbWithAura
|
||
or UnitSelectorKind.HighestHealingAbsorbWithAuraCount
|
||
? UnitSelectorKind.HighestHealingAbsorb
|
||
: kind;
|
||
|
||
private void SelectLowestHealthAuraFilter(UnitSelectorKind kind)
|
||
{
|
||
var filter = kind switch
|
||
{
|
||
UnitSelectorKind.LowestHealthWithAnyAura => LowestHealthAuraFilterKind.WithAnyAura,
|
||
UnitSelectorKind.LowestHealthWithoutAnyAura => LowestHealthAuraFilterKind.WithoutAnyAura,
|
||
UnitSelectorKind.LowestHealthWithoutAura => LowestHealthAuraFilterKind.WithoutAura,
|
||
UnitSelectorKind.LowestHealthWithAura => LowestHealthAuraFilterKind.WithAura,
|
||
UnitSelectorKind.LowestHealthWithAuraCount => LowestHealthAuraFilterKind.WithAuraCount,
|
||
UnitSelectorKind.HighestHealingAbsorbWithAnyAura => LowestHealthAuraFilterKind.WithAnyAura,
|
||
UnitSelectorKind.HighestHealingAbsorbWithoutAnyAura => LowestHealthAuraFilterKind.WithoutAnyAura,
|
||
UnitSelectorKind.HighestHealingAbsorbWithoutAura => LowestHealthAuraFilterKind.WithoutAura,
|
||
UnitSelectorKind.HighestHealingAbsorbWithAura => LowestHealthAuraFilterKind.WithAura,
|
||
UnitSelectorKind.HighestHealingAbsorbWithAuraCount => LowestHealthAuraFilterKind.WithAuraCount,
|
||
_ => LowestHealthAuraFilterKind.None
|
||
};
|
||
|
||
for (var i = 0; i < _lowestHealthAuraFilterBox.Items.Count; i++)
|
||
{
|
||
if (_lowestHealthAuraFilterBox.Items[i] is LowestHealthAuraFilterItem item && item.Kind == filter)
|
||
{
|
||
_lowestHealthAuraFilterBox.SelectedIndex = i;
|
||
return;
|
||
}
|
||
}
|
||
|
||
_lowestHealthAuraFilterBox.SelectedIndex = 0;
|
||
}
|
||
|
||
private void SelectLowestHealthRoleFilter(UnitRoleFilterKind? kind)
|
||
{
|
||
for (var i = 0; i < _lowestHealthRoleFilterBox.Items.Count; i++)
|
||
{
|
||
if (_lowestHealthRoleFilterBox.Items[i] is LowestHealthRoleFilterItem item && item.Kind == kind)
|
||
{
|
||
_lowestHealthRoleFilterBox.SelectedIndex = i;
|
||
return;
|
||
}
|
||
}
|
||
|
||
_lowestHealthRoleFilterBox.SelectedIndex = 0;
|
||
}
|
||
|
||
private void SelectSelector(UnitSelectorKind kind)
|
||
{
|
||
for (var i = 0; i < _selectorBox.Items.Count; i++)
|
||
{
|
||
if (_selectorBox.Items[i] is SelectorItem item && item.Kind == kind)
|
||
{
|
||
_selectorBox.SelectedIndex = i;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void SelectSelector(CountKind kind)
|
||
{
|
||
for (var i = 0; i < _selectorBox.Items.Count; i++)
|
||
{
|
||
if (_selectorBox.Items[i] is CountItem item && item.Kind == kind)
|
||
{
|
||
_selectorBox.SelectedIndex = i;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void SeedThresholdField(string? field)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(field))
|
||
{
|
||
SelectThresholdMode(usesDynamicField: false);
|
||
return;
|
||
}
|
||
|
||
SelectThresholdMode(usesDynamicField: true);
|
||
SelectThresholdField(field.Trim());
|
||
}
|
||
|
||
private void SelectThresholdMode(bool usesDynamicField)
|
||
{
|
||
for (var i = 0; i < _thresholdModeBox.Items.Count; i++)
|
||
{
|
||
if (_thresholdModeBox.Items[i] is ThresholdModeItem item && item.UsesDynamicField == usesDynamicField)
|
||
{
|
||
_thresholdModeBox.SelectedIndex = i;
|
||
return;
|
||
}
|
||
}
|
||
|
||
_thresholdModeBox.SelectedIndex = 0;
|
||
}
|
||
|
||
private void SelectThresholdField(string field)
|
||
{
|
||
var index = _thresholdFieldBox.Items.IndexOf(field);
|
||
if (index < 0)
|
||
{
|
||
_thresholdFieldBox.Items.Add(field);
|
||
index = _thresholdFieldBox.Items.Count - 1;
|
||
}
|
||
|
||
_thresholdFieldBox.SelectedIndex = index;
|
||
}
|
||
|
||
private void SelectRole(int role)
|
||
{
|
||
for (var i = 0; i < _roleBox.Items.Count; i++)
|
||
{
|
||
if (_roleBox.Items[i] is RoleOption option && option.Value == role)
|
||
{
|
||
_roleBox.SelectedIndex = i;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void SelectDispelType(int dispelType)
|
||
{
|
||
for (var i = 0; i < _dispelTypeBox.Items.Count; i++)
|
||
{
|
||
if (_dispelTypeBox.Items[i] is DispelTypeOption option && option.Value == dispelType)
|
||
{
|
||
_dispelTypeBox.SelectedIndex = i;
|
||
return;
|
||
}
|
||
}
|
||
|
||
_dispelTypeBox.SelectedIndex = 0;
|
||
}
|
||
|
||
private static void SelectAura(ComboBox box, string? aura)
|
||
{
|
||
if (string.IsNullOrEmpty(aura))
|
||
{
|
||
return;
|
||
}
|
||
|
||
var index = box.Items.IndexOf(aura);
|
||
if (index < 0)
|
||
{
|
||
box.Items.Add(aura);
|
||
index = box.Items.Count - 1;
|
||
}
|
||
|
||
box.SelectedIndex = index;
|
||
}
|
||
|
||
private void CheckAuras(List<string>? auras)
|
||
{
|
||
if (auras is null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
foreach (var aura in auras)
|
||
{
|
||
var index = _aurasBox.Items.IndexOf(aura);
|
||
if (index < 0)
|
||
{
|
||
index = _aurasBox.Items.Add(aura);
|
||
}
|
||
|
||
_aurasBox.SetItemChecked(index, true);
|
||
}
|
||
}
|
||
|
||
private static decimal Clamp(int value, NumericUpDown box)
|
||
{
|
||
return Math.Clamp(value, (int)box.Minimum, (int)box.Maximum);
|
||
}
|
||
|
||
private static Panel BuildLabeledRow(string label, Control control, int height = 44)
|
||
{
|
||
var panel = new Panel
|
||
{
|
||
Width = RowWidth,
|
||
Height = height,
|
||
BackColor = UiTheme.SurfaceRaised,
|
||
Margin = new Padding(0, 1, 0, 5)
|
||
};
|
||
|
||
var labelControl = new Label
|
||
{
|
||
Text = label,
|
||
ForeColor = UiTheme.Muted,
|
||
TextAlign = ContentAlignment.MiddleLeft,
|
||
Bounds = new Rectangle(0, height > 50 ? 4 : Math.Max(0, (height - 24) / 2), LabelWidth, 24),
|
||
AutoEllipsis = true
|
||
};
|
||
|
||
control.Bounds = new Rectangle(ControlLeft, 3, RowWidth - ControlLeft, height - 6);
|
||
panel.Controls.Add(control);
|
||
panel.Controls.Add(labelControl);
|
||
return panel;
|
||
}
|
||
|
||
private Control BuildSplitRow(string labelA, Control controlA, string labelB, Control controlB)
|
||
{
|
||
var panel = new Panel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
BackColor = UiTheme.SurfaceRaised,
|
||
Margin = new Padding(0)
|
||
};
|
||
|
||
var labelAControl = new Label { Text = labelA, ForeColor = UiTheme.Muted, TextAlign = ContentAlignment.MiddleLeft, Bounds = new Rectangle(0, 5, 72, 28), AutoEllipsis = true };
|
||
controlA.Bounds = new Rectangle(80, 5, 230, 28);
|
||
var labelBControl = new Label { Text = labelB, ForeColor = UiTheme.Muted, TextAlign = ContentAlignment.MiddleLeft, Bounds = new Rectangle(330, 5, 130, 28), AutoEllipsis = true };
|
||
controlB.Bounds = new Rectangle(466, 5, RowWidth - 466, 28);
|
||
|
||
panel.Controls.Add(controlA);
|
||
panel.Controls.Add(labelAControl);
|
||
panel.Controls.Add(controlB);
|
||
panel.Controls.Add(labelBControl);
|
||
return panel;
|
||
}
|
||
|
||
private Control BuildNameRow()
|
||
{
|
||
var panel = new Panel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
BackColor = UiTheme.SurfaceRaised,
|
||
Margin = new Padding(0)
|
||
};
|
||
|
||
var nameLabel = new Label { Text = "名称", ForeColor = UiTheme.Muted, TextAlign = ContentAlignment.MiddleLeft, Bounds = new Rectangle(0, 5, 72, 28), AutoEllipsis = true };
|
||
UiTheme.StyleTextBox(_nameBox);
|
||
_nameBox.Bounds = new Rectangle(80, 5, 230, 28);
|
||
_healthNameLabel.Text = "值名称";
|
||
_healthNameLabel.ForeColor = UiTheme.Muted;
|
||
_healthNameLabel.TextAlign = ContentAlignment.MiddleLeft;
|
||
_healthNameLabel.Bounds = new Rectangle(330, 5, 130, 28);
|
||
_healthNameLabel.AutoEllipsis = true;
|
||
UiTheme.StyleTextBox(_healthNameBox);
|
||
_healthNameBox.Bounds = new Rectangle(466, 5, RowWidth - 466, 28);
|
||
_toolTip.SetToolTip(_healthNameBox, "可选:把该单位生命值暴露为同名数值条件字段(如 最低血量 < 50)");
|
||
_toolTip.SetToolTip(_healthNameLabel, "可选:把该单位生命值暴露为同名数值条件字段(如 最低血量 < 50)");
|
||
|
||
panel.Controls.Add(_nameBox);
|
||
panel.Controls.Add(nameLabel);
|
||
panel.Controls.Add(_healthNameBox);
|
||
panel.Controls.Add(_healthNameLabel);
|
||
return panel;
|
||
}
|
||
|
||
private sealed record SelectorItem(string Text, UnitSelectorKind Kind)
|
||
{
|
||
public override string ToString() => Text;
|
||
}
|
||
|
||
private sealed record LowestHealthAuraFilterItem(string Text, LowestHealthAuraFilterKind Kind)
|
||
{
|
||
public override string ToString() => Text;
|
||
}
|
||
|
||
private sealed record LowestHealthRoleFilterItem(string Text, UnitRoleFilterKind? Kind)
|
||
{
|
||
public override string ToString() => Text;
|
||
}
|
||
|
||
private sealed record CountItem(string Text, CountKind Kind)
|
||
{
|
||
public override string ToString() => Text;
|
||
}
|
||
|
||
private sealed record ThresholdModeItem(string Text, bool UsesDynamicField)
|
||
{
|
||
public override string ToString() => Text;
|
||
}
|
||
|
||
private sealed record RoleOption(string Text, int Value)
|
||
{
|
||
public override string ToString() => Text;
|
||
}
|
||
|
||
private sealed record DispelTypeOption(string Text, int Value)
|
||
{
|
||
public override string ToString() => Text;
|
||
}
|
||
|
||
private enum LowestHealthAuraFilterKind
|
||
{
|
||
None,
|
||
WithAnyAura,
|
||
WithoutAnyAura,
|
||
WithoutAura,
|
||
WithAura,
|
||
WithAuraCount
|
||
}
|
||
}
|