using System.Drawing;
namespace Shigure;
///
/// 动态单位 / 数量字段的编辑弹窗: 选择类别(单位/数量)与选择器, 按所选类型动态显隐参数控件。
/// 光环候选来自当前职业/专精的 group 字段。校验名称非空、唯一、非纯数字、不含 '.'/'$'。
///
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 _auraFields;
private readonly IReadOnlyList _thresholdFields;
private readonly HashSet _takenNames;
private readonly Label _healthNameLabel = new();
private readonly TextBox _nameBox = new();
private readonly TextBox _healthNameBox = new();
private readonly UiDropDown _categoryBox = new();
private readonly UiDropDown _selectorBox = new();
private readonly UiDropDown _lowestHealthAuraFilterBox = new();
private readonly UiDropDown _lowestHealthRoleFilterBox = new();
private readonly FlowLayoutPanel _paramPanel = new();
private readonly Label _previewLabel = new();
private readonly ToolTip _toolTip = new();
private readonly NumericUpDown _thresholdBox = new();
private readonly UiDropDown _thresholdModeBox = new();
private readonly UiDropDown _thresholdFieldBox = new();
private readonly UiDropDown _roleBox = new();
private readonly CheckBox _reverseBox = new();
private readonly UiDropDown _auraBox = new();
private readonly CheckedListBox _aurasBox = new();
private readonly NumericUpDown _auraCountBox = new();
private readonly UiDropDown _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 auraFields,
IReadOnlyList thresholdFields,
IReadOnlyCollection takenNames,
ModuleUnit? existingUnit,
ModuleCountField? existingCount)
{
_auraFields = auraFields;
_thresholdFields = thresholdFields;
_takenNames = new HashSet(takenNames, StringComparer.OrdinalIgnoreCase);
InitializeComponent();
Seed(existingUnit, existingCount);
UpdateParamVisibility();
UpdateHealthNameState();
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
UiTheme.ApplyDarkTitleBar(this);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
RestoreCachedWindowSize();
}
protected override void OnResizeEnd(EventArgs e)
{
base.OnResizeEnd(e);
SaveWindowSize();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
SaveWindowSize();
base.OnFormClosed(e);
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
_nameBox.Focus();
_nameBox.SelectAll();
}
private void RestoreCachedWindowSize()
{
var cached = UiCacheStore.Load().UnitEditorWindowSize;
if (cached is null || cached.Width <= 0 || cached.Height <= 0)
{
return;
}
var workingArea = Owner is not null
? Screen.FromControl(Owner).WorkingArea
: Screen.FromControl(this).WorkingArea;
var maximumWidth = Math.Max(MinimumSize.Width, workingArea.Width - 40);
var maximumHeight = Math.Max(MinimumSize.Height, workingArea.Height - 40);
Size = new Size(
Math.Clamp(cached.Width, MinimumSize.Width, maximumWidth),
Math.Clamp(cached.Height, MinimumSize.Height, maximumHeight));
if (Owner is not null)
{
CenterToParent();
}
else
{
CenterToScreen();
}
}
private void SaveWindowSize()
{
if (WindowState != FormWindowState.Normal || Width <= 0 || Height <= 0)
{
return;
}
var cache = UiCacheStore.Load();
cache.UnitEditorWindowSize = new WindowSize
{
Width = Width,
Height = Height
};
UiCacheStore.Save(cache);
}
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.Sizable;
MaximizeBox = false;
MinimizeBox = false;
ShowInTaskbar = false;
StartPosition = FormStartPosition.CenterParent;
MinimumSize = new Size(RowWidth + 52, 420);
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