/
Yaroslav4444-bauer
/
ArmyFight
Обзор
Документация
Войти
/
Yaroslav4444-bauer
/
ArmyFight
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
ViewModels/SetupViewModel.cs
410 строк
15 KB
Yaroslav4444-bauer
Добовлены комментарии summary
01 июн 2026, 12:38
01 июн 2026, 12:38
70196b7
Код
Авторство
О чём код?
using System.Collections.ObjectModel; using System.Windows.Media; using ArmyFight.Services; using WpfCommand = System.Windows.Input.ICommand; namespace ArmyFight.ViewModels { /// <summary> /// Один слот в редакторе ручного порядка построения армии. /// </summary> public class LineupSlotViewModel : BaseViewModel { private readonly ObservableCollection<LineupSlotViewModel> _lineup; public string UnitName { get; } public string Symbol { get; } public int CatalogIndex { get; } public Brush BadgeBrush { get; } private int _position; public int Position { get => _position; set => Set(ref _position, value); } public WpfCommand MoveUpCommand { get; } public WpfCommand MoveDownCommand { get; } /// <summary> /// Создаёт слот с символом, цветом и командами сдвига в списке построения. /// </summary> /// <param name="def">Определение типа юнита в каталоге.</param> /// <param name="catalogIndex">Индекс в <see cref="UnitCatalog.All"/>.</param> /// <param name="lineup">Общая коллекция слотов для перемещения.</param> public LineupSlotViewModel(UnitDefinition def, int catalogIndex, ObservableCollection<LineupSlotViewModel> lineup) { _lineup = lineup; UnitName = def.MenuName; CatalogIndex = catalogIndex; Symbol = catalogIndex switch { 0 => "L", 1 => "H", 2 => "A", 3 => "D", 4 => "M", 5 => "G", _ => "?" }; BadgeBrush = catalogIndex switch { 0 => new SolidColorBrush(Color.FromRgb(79, 195, 247)), 1 => new SolidColorBrush(Color.FromRgb(239, 83, 80)), 2 => new SolidColorBrush(Color.FromRgb(102, 187, 106)), 3 => new SolidColorBrush(Color.FromRgb(38, 198, 218)), 4 => new SolidColorBrush(Color.FromRgb(171, 71, 188)), 5 => new SolidColorBrush(Color.FromRgb(120, 144, 156)), _ => new SolidColorBrush(Color.FromRgb(139, 153, 181)) }; MoveUpCommand = new RelayCommand(() => { int idx = _lineup.IndexOf(this); if (idx > 0) { _lineup.Move(idx, idx - 1); RefreshPositions(); } }); MoveDownCommand = new RelayCommand(() => { int idx = _lineup.IndexOf(this); if (idx < _lineup.Count - 1) { _lineup.Move(idx, idx + 1); RefreshPositions(); } }); } /// <summary> /// Обновляет номера позиций всех слотов после изменения порядка. /// </summary> private void RefreshPositions() { for (int i = 0; i < _lineup.Count; i++) _lineup[i].Position = i + 1; } } /// <summary> /// Строка редактора количества для одного типа юнита. /// </summary> public class UnitTypeSetupViewModel : BaseViewModel { public string Name { get; } public int UnitCost { get; } private int _count; public int Count { get => _count; set => Set(ref _count, Math.Max(0, value)); } public WpfCommand IncrCommand { get; } public WpfCommand DecrCommand { get; } /// <summary> /// Создаёт строку с командами +/- с проверкой остатка бюджета. /// </summary> /// <param name="def">Тип юнита.</param> /// <param name="getRemainingCost">Функция остатка монет армии.</param> public UnitTypeSetupViewModel(UnitDefinition def, Func<int> getRemainingCost) { Name = def.MenuName; UnitCost = def.Cost; IncrCommand = new RelayCommand( () => Count++, () => getRemainingCost() >= UnitCost); DecrCommand = new RelayCommand( () => Count--, () => Count > 0); } } /// <summary> /// Модель экрана настройки игры: бюджет, режим боя, состав и порядок армий. /// </summary> public class SetupViewModel : BaseViewModel { private readonly GameService _svc; private readonly MainViewModel _nav; // ── Общие настройки ────────────────────────────────── private int _armyCost = 100; public int ArmyCost { get => _armyCost; set { if (Set(ref _armyCost, Math.Max(1, value))) { Notify(nameof(Army1RemainingCost)); Notify(nameof(Army2RemainingCost)); } } } private bool _is1to1 = true; public bool Is1to1 { get => _is1to1; set => Set(ref _is1to1, value); } private bool _is3to3; public bool Is3to3 { get => _is3to3; set => Set(ref _is3to3, value); } private bool _isLineToLine; public bool IsLineToLine { get => _isLineToLine; set => Set(ref _isLineToLine, value); } public bool IsQuickGame { get; } // ── Армия 1 ─────────────────────────────────────────── private bool _a1IsRandom = true; public bool A1IsRandom { get => _a1IsRandom; set { if (Set(ref _a1IsRandom, value)) Notify(nameof(A1ShowUnits)); } } private bool _a1IsManual; public bool A1IsManual { get => _a1IsManual; set { if (Set(ref _a1IsManual, value)) { Notify(nameof(A1ShowUnits)); Notify(nameof(A1ShowLineup)); if (value) RebuildArmy1Lineup(); } } } private bool _a1IsFullRandom; public bool A1IsFullRandom { get => _a1IsFullRandom; set { if (Set(ref _a1IsFullRandom, value)) Notify(nameof(A1ShowUnits)); } } public bool A1ShowUnits => A1IsRandom || A1IsManual; public bool A1ShowLineup => A1IsManual && Army1Lineup.Count > 0; public ObservableCollection<UnitTypeSetupViewModel> Army1Units { get; } = new(); public ObservableCollection<LineupSlotViewModel> Army1Lineup { get; } = new(); public int Army1RemainingCost => ArmyCost - Army1Units.Sum(u => u.Count * u.UnitCost); // ── Армия 2 ─────────────────────────────────────────── private bool _a2IsRandom = true; public bool A2IsRandom { get => _a2IsRandom; set { if (Set(ref _a2IsRandom, value)) Notify(nameof(A2ShowUnits)); } } private bool _a2IsManual; public bool A2IsManual { get => _a2IsManual; set { if (Set(ref _a2IsManual, value)) { Notify(nameof(A2ShowUnits)); Notify(nameof(A2ShowLineup)); if (value) RebuildArmy2Lineup(); } } } private bool _a2IsFullRandom; public bool A2IsFullRandom { get => _a2IsFullRandom; set { if (Set(ref _a2IsFullRandom, value)) Notify(nameof(A2ShowUnits)); } } public bool A2ShowUnits => A2IsRandom || A2IsManual; public bool A2ShowLineup => A2IsManual && Army2Lineup.Count > 0; public ObservableCollection<UnitTypeSetupViewModel> Army2Units { get; } = new(); public ObservableCollection<LineupSlotViewModel> Army2Lineup { get; } = new(); public int Army2RemainingCost => ArmyCost - Army2Units.Sum(u => u.Count * u.UnitCost); // ── Команды ─────────────────────────────────────────── public WpfCommand StartCommand { get; } public WpfCommand BackCommand { get; } /// <summary> /// Инициализирует редакторы армий, подписки на Count и команды старта/назад. /// </summary> /// <param name="nav">Навигация.</param> /// <param name="svc">Сервис игры.</param> /// <param name="isQuickGame">Режим быстрой игры (только бюджет и стратегия).</param> public SetupViewModel(MainViewModel nav, GameService svc, bool isQuickGame) { _nav = nav; _svc = svc; IsQuickGame = isQuickGame; foreach (var def in UnitCatalog.All) { var vm1 = new UnitTypeSetupViewModel(def, () => Army1RemainingCost); vm1.PropertyChanged += (_, e) => { if (e.PropertyName != nameof(UnitTypeSetupViewModel.Count)) return; Notify(nameof(Army1RemainingCost)); if (A1IsManual) { RebuildArmy1Lineup(); Notify(nameof(A1ShowLineup)); } }; Army1Units.Add(vm1); var vm2 = new UnitTypeSetupViewModel(def, () => Army2RemainingCost); vm2.PropertyChanged += (_, e) => { if (e.PropertyName != nameof(UnitTypeSetupViewModel.Count)) return; Notify(nameof(Army2RemainingCost)); if (A2IsManual) { RebuildArmy2Lineup(); Notify(nameof(A2ShowLineup)); } }; Army2Units.Add(vm2); } StartCommand = new RelayCommand(DoStart, CanStart); BackCommand = new RelayCommand(() => nav.ShowMenu()); } /// <summary> /// Проверяет, можно ли начать игру: положительный бюджет и неотрицательный остаток у армий. /// </summary> private bool CanStart() { if (IsQuickGame) return ArmyCost > 0; bool a1ok = A1IsFullRandom || Army1RemainingCost >= 0; bool a2ok = A2IsFullRandom || Army2RemainingCost >= 0; return ArmyCost > 0 && a1ok && a2ok; } /// <summary> /// Собирает конфигурацию, создаёт игру и переходит к экрану боя. /// </summary> private void DoStart() { var cfg = BuildConfig(); _svc.SetupNewGame(cfg); _nav.ShowBattle(); } /// <summary> /// Формирует <see cref="GameSetupConfig"/> из текущих настроек UI. /// </summary> private GameSetupConfig BuildConfig() { string strategy = Is3to3 ? "3to3" : IsLineToLine ? "linetoline" : "1to1"; if (IsQuickGame) { return new GameSetupConfig { ArmyCost = ArmyCost, BattleStrategy = strategy, Army1BuildMethod = "fullyrandom", Army2BuildMethod = "fullyrandom" }; } string a1method = A1IsFullRandom ? "fullyrandom" : A1IsManual ? "manual" : "random"; string a2method = A2IsFullRandom ? "fullyrandom" : A2IsManual ? "manual" : "random"; return new GameSetupConfig { ArmyCost = ArmyCost, BattleStrategy = strategy, Army1BuildMethod = a1method, A1Light = Army1Units[0].Count, A1Heavy = Army1Units[1].Count, A1Archer = Army1Units[2].Count, A1Doctor = Army1Units[3].Count, A1Mage = Army1Units[4].Count, A1Gulyay = Army1Units[5].Count, A1LineupOrder = A1IsManual && Army1Lineup.Count > 0 ? Army1Lineup.Select(s => s.CatalogIndex).ToList() : null, Army2BuildMethod = a2method, A2Light = Army2Units[0].Count, A2Heavy = Army2Units[1].Count, A2Archer = Army2Units[2].Count, A2Doctor = Army2Units[3].Count, A2Mage = Army2Units[4].Count, A2Gulyay = Army2Units[5].Count, A2LineupOrder = A2IsManual && Army2Lineup.Count > 0 ? Army2Lineup.Select(s => s.CatalogIndex).ToList() : null }; } /// <summary> /// Перестраивает слоты порядка армии 1 по текущим счётчикам типов. /// </summary> private void RebuildArmy1Lineup() => RebuildLineup(Army1Lineup, Army1Units); /// <summary> /// Перестраивает слоты порядка армии 2 по текущим счётчикам типов. /// </summary> private void RebuildArmy2Lineup() => RebuildLineup(Army2Lineup, Army2Units); /// <summary> /// Синхронизирует коллекцию слотов построения с количеством юнитов каждого типа. /// </summary> /// <param name="lineup">Коллекция слотов.</param> /// <param name="units">Счётчики по типам.</param> private static void RebuildLineup( ObservableCollection<LineupSlotViewModel> lineup, ObservableCollection<UnitTypeSetupViewModel> units) { for (int i = 0; i < units.Count; i++) { var def = UnitCatalog.All[i]; int target = units[i].Count; int current = lineup.Count(s => s.CatalogIndex == i); if (current > target) { int toRemove = current - target; for (int j = lineup.Count - 1; j >= 0 && toRemove > 0; j--) { if (lineup[j].CatalogIndex == i) { lineup.RemoveAt(j); toRemove--; } } } else if (current < target) { for (int k = 0; k < target - current; k++) lineup.Add(new LineupSlotViewModel(def, i, lineup)); } } for (int i = 0; i < lineup.Count; i++) lineup[i].Position = i + 1; } } }