98 lines
3.5 KiB
C#
98 lines
3.5 KiB
C#
using YamlDotNet.Serialization;
|
||
using YamlDotNet.Serialization.NamingConventions;
|
||
|
||
namespace Vanguard.Content;
|
||
|
||
public sealed record TrialDeckCard(string PrintingId, int Count);
|
||
|
||
public sealed record TrialDeckDefinition(
|
||
string Id,
|
||
string Name,
|
||
string? Clan,
|
||
IReadOnlyList<TrialDeckCard> Cards)
|
||
{
|
||
public int TotalCards => Cards.Sum(card => card.Count);
|
||
|
||
/// <summary>Разворачивает counts в список printingId длиной 50.</summary>
|
||
public IReadOnlyList<string> ToDeck() =>
|
||
Cards.SelectMany(card => Enumerable.Repeat(card.PrintingId, card.Count)).ToList();
|
||
}
|
||
|
||
/// <summary>Две триал-деки из content/trial-decks.yaml.</summary>
|
||
public sealed class TrialDeckCatalog
|
||
{
|
||
private readonly Dictionary<string, TrialDeckDefinition> _decks;
|
||
|
||
private TrialDeckCatalog(IEnumerable<TrialDeckDefinition> decks)
|
||
{
|
||
_decks = decks.ToDictionary(deck => deck.Id, StringComparer.OrdinalIgnoreCase);
|
||
}
|
||
|
||
public IReadOnlyList<TrialDeckDefinition> Decks => _decks.Values.ToList();
|
||
|
||
public bool TryGet(string id, out TrialDeckDefinition deck) =>
|
||
_decks.TryGetValue(id, out deck!);
|
||
|
||
public static TrialDeckCatalog Load(string path)
|
||
{
|
||
if (!File.Exists(path))
|
||
throw new FileNotFoundException($"Файл триал-дек не найден: {path}");
|
||
|
||
var deserializer = new DeserializerBuilder()
|
||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||
.IgnoreUnmatchedProperties()
|
||
.Build();
|
||
|
||
var file = deserializer.Deserialize<TrialDeckFile>(File.ReadAllText(path))
|
||
?? throw new InvalidOperationException($"Пустой файл триал-дек: {path}");
|
||
|
||
if (file.Decks.Count == 0)
|
||
throw new InvalidOperationException("В trial-decks.yaml нет дек.");
|
||
|
||
var decks = new List<TrialDeckDefinition>();
|
||
var ids = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||
|
||
foreach (var entry in file.Decks)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(entry.Id) || string.IsNullOrWhiteSpace(entry.Name))
|
||
throw new InvalidOperationException("У триал-деки должны быть id и name.");
|
||
|
||
if (!ids.Add(entry.Id))
|
||
throw new InvalidOperationException($"Дубликат id триал-деки: {entry.Id}");
|
||
|
||
var cards = entry.Cards
|
||
.Where(card => !string.IsNullOrWhiteSpace(card.PrintingId) && card.Count > 0)
|
||
.Select(card => new TrialDeckCard(card.PrintingId, card.Count))
|
||
.ToList();
|
||
|
||
var total = cards.Sum(card => card.Count);
|
||
if (total != 50)
|
||
throw new InvalidOperationException(
|
||
$"Триал-дека {entry.Id}: {total} карт, требуется 50.");
|
||
|
||
decks.Add(new TrialDeckDefinition(entry.Id, entry.Name, entry.Clan, cards));
|
||
}
|
||
|
||
return new TrialDeckCatalog(decks);
|
||
}
|
||
|
||
private sealed class TrialDeckFile
|
||
{
|
||
public List<TrialDeckEntry> Decks { get; set; } = new();
|
||
}
|
||
|
||
private sealed class TrialDeckEntry
|
||
{
|
||
public string Id { get; set; } = string.Empty;
|
||
public string Name { get; set; } = string.Empty;
|
||
public string? Clan { get; set; }
|
||
public List<TrialDeckCardEntry> Cards { get; set; } = new();
|
||
}
|
||
|
||
private sealed class TrialDeckCardEntry
|
||
{
|
||
public string PrintingId { get; set; } = string.Empty;
|
||
public int Count { get; set; }
|
||
}
|
||
}
|