Critical tables page

This commit is contained in:
2026-03-14 12:38:19 +01:00
parent b399840671
commit e4e8995fd8
10 changed files with 828 additions and 103 deletions

View File

@@ -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();
}