Critical tables page
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RolemasterDb.App.Data;
|
||||
using RolemasterDb.App.Domain;
|
||||
|
||||
namespace RolemasterDb.App.Features;
|
||||
|
||||
@@ -178,6 +182,158 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
.SingleOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<CriticalTableDetail?> GetCriticalTableAsync(string slug, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await using var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var table = await dbContext.CriticalTables
|
||||
.AsNoTracking()
|
||||
.AsSplitQuery()
|
||||
.Include(item => item.Columns)
|
||||
.Include(item => item.Groups)
|
||||
.Include(item => item.RollBands)
|
||||
.Include(item => item.Results)
|
||||
.ThenInclude(result => result.CriticalColumn)
|
||||
.Include(item => item.Results)
|
||||
.ThenInclude(result => result.CriticalGroup)
|
||||
.Include(item => item.Results)
|
||||
.ThenInclude(result => result.CriticalRollBand)
|
||||
.Include(item => item.Results)
|
||||
.ThenInclude(result => result.Effects)
|
||||
.Include(item => item.Results)
|
||||
.ThenInclude(result => result.Branches)
|
||||
.ThenInclude(branch => branch.Effects)
|
||||
.Where(item => item.Slug == slug)
|
||||
.SingleOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (table is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var columns = table.Columns
|
||||
.OrderBy(column => column.SortOrder)
|
||||
.Select(column => new CriticalColumnReference(column.ColumnKey, column.Label, column.Role, column.SortOrder))
|
||||
.ToList();
|
||||
|
||||
var groups = table.Groups
|
||||
.OrderBy(group => group.SortOrder)
|
||||
.Select(group => new CriticalGroupReference(group.GroupKey, group.Label, group.SortOrder))
|
||||
.ToList();
|
||||
|
||||
var rollBands = table.RollBands
|
||||
.OrderBy(rollBand => rollBand.SortOrder)
|
||||
.Select(rollBand => new CriticalRollBandReference(rollBand.Label, rollBand.MinRoll, rollBand.MaxRoll, rollBand.SortOrder))
|
||||
.ToList();
|
||||
|
||||
var cells = table.Results
|
||||
.OrderBy(result => result.CriticalRollBand.SortOrder)
|
||||
.ThenBy(result => result.CriticalGroup?.SortOrder ?? 0)
|
||||
.ThenBy(result => result.CriticalColumn.SortOrder)
|
||||
.Select(result => new CriticalTableCellDetail(
|
||||
result.CriticalRollBand.Label,
|
||||
result.CriticalColumn.ColumnKey,
|
||||
result.CriticalColumn.Label,
|
||||
result.CriticalColumn.Role,
|
||||
result.CriticalGroup?.GroupKey,
|
||||
result.CriticalGroup?.Label,
|
||||
result.DescriptionText,
|
||||
result.Effects
|
||||
.OrderBy(effect => effect.Id)
|
||||
.Select(effect => CreateEffectLookupResponse(effect))
|
||||
.ToList(),
|
||||
result.Branches
|
||||
.OrderBy(branch => branch.SortOrder)
|
||||
.Select(branch => CreateBranchLookupResponse(branch))
|
||||
.ToList()))
|
||||
.ToList();
|
||||
|
||||
var legend = BuildLegend(cells);
|
||||
|
||||
return new CriticalTableDetail(
|
||||
table.Slug,
|
||||
table.DisplayName,
|
||||
table.Family,
|
||||
table.SourceDocument,
|
||||
table.Notes,
|
||||
columns,
|
||||
groups,
|
||||
rollBands,
|
||||
cells,
|
||||
legend);
|
||||
}
|
||||
private static IReadOnlyList<CriticalTableLegendEntry> BuildLegend(IReadOnlyList<CriticalTableCellDetail> cells)
|
||||
{
|
||||
var seenCodes = new HashSet<string>(StringComparer.Ordinal);
|
||||
var legend = new List<CriticalTableLegendEntry>();
|
||||
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
var baseEffects = cell.Effects ?? Array.Empty<CriticalEffectLookupResponse>();
|
||||
foreach (var effect in baseEffects)
|
||||
{
|
||||
TryAddLegendEntry(effect.EffectCode);
|
||||
}
|
||||
|
||||
var branches = cell.Branches ?? Array.Empty<CriticalBranchLookupResponse>();
|
||||
foreach (var branch in branches)
|
||||
{
|
||||
var branchEffects = branch.Effects ?? Array.Empty<CriticalEffectLookupResponse>();
|
||||
foreach (var effect in branchEffects)
|
||||
{
|
||||
TryAddLegendEntry(effect.EffectCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return legend
|
||||
.OrderBy(item => item.Label, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
void TryAddLegendEntry(string effectCode)
|
||||
{
|
||||
if (!seenCodes.Add(effectCode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AffixDisplayMap.TryGet(effectCode, out var info))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
legend.Add(new CriticalTableLegendEntry(effectCode, info.Symbol, info.Label, info.Description, info.Tooltip));
|
||||
}
|
||||
}
|
||||
|
||||
private static CriticalEffectLookupResponse CreateEffectLookupResponse(CriticalEffect effect) =>
|
||||
new(
|
||||
effect.EffectCode,
|
||||
effect.Target,
|
||||
effect.ValueInteger,
|
||||
effect.ValueExpression,
|
||||
effect.DurationRounds,
|
||||
effect.PerRound,
|
||||
effect.Modifier,
|
||||
effect.BodyPart,
|
||||
effect.IsPermanent,
|
||||
effect.SourceType,
|
||||
effect.SourceText);
|
||||
|
||||
private static CriticalBranchLookupResponse CreateBranchLookupResponse(CriticalBranch branch) =>
|
||||
new(
|
||||
branch.BranchKind,
|
||||
branch.ConditionKey,
|
||||
branch.ConditionText,
|
||||
branch.DescriptionText,
|
||||
branch.RawAffixText,
|
||||
(branch.Effects ?? Enumerable.Empty<CriticalEffect>())
|
||||
.OrderBy(effect => effect.Id)
|
||||
.Select(effect => CreateEffectLookupResponse(effect))
|
||||
.ToList(),
|
||||
branch.RawText,
|
||||
branch.SortOrder);
|
||||
|
||||
private static string NormalizeSlug(string value) =>
|
||||
value.Trim().Replace(' ', '_').ToLowerInvariant();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user