@page "/" @rendermode InteractiveServer @inject LookupService LookupService Lookup Desk @if (referenceData is null) {

Summoning tables...

Loading the starter attack and critical data from SQLite.

} else {
Rolemaster Lookup Desk

Resolve the attack roll, then the critical, from one place.

Attack tables still come from the starter dataset, while critical lookups now read the importer-managed tables loaded into the same SQLite file. The page surfaces both the gameplay result and the import metadata behind each critical entry.

@referenceData.AttackTables.Count attack tables @referenceData.CriticalTables.Count critical tables @referenceData.ArmorTypes.Count armor types SQLite file: rolemaster.db

Automatic Attack Lookup

Choose an attack table, armor type, and attack roll. If the attack produces a critical and you provide the critical roll, the app resolves that follow-up automatically.

Leave critical roll blank to stop after the attack table result.
@if (!string.IsNullOrWhiteSpace(attackError)) {

@attackError

} @if (attackResult is not null) {

@attackResult.AttackTableName vs @attackResult.ArmorTypeLabel

Roll band: @attackResult.RollBand Hits: @attackResult.Hits @if (!string.IsNullOrWhiteSpace(attackResult.CriticalSeverity)) { @attackResult.CriticalSeverity @attackResult.CriticalType critical } else { No critical }

Table notation: @attackResult.RawNotation

@if (!string.IsNullOrWhiteSpace(attackResult.Notes)) {

@attackResult.Notes

} @if (attackResult.AutoCritical is not null) {

Automatic critical resolution

} else if (!string.IsNullOrWhiteSpace(attackResult.CriticalSeverity)) {
The attack produced a critical. Add a critical roll to resolve it automatically.
}
}

Direct Critical Lookup

Use this when you already know the critical table, column, roll, and group if the selected table has variants.

@if (SelectedCriticalTable?.Groups.Count > 0) {
}
@if (!string.IsNullOrWhiteSpace(criticalError)) {

@criticalError

} @if (criticalResult is not null) {
}

Loaded Reference Data

Attack tables remain starter content. Critical tables below are whatever importer-managed entries are currently loaded into the app database.

@foreach (var attackTable in referenceData.AttackTables) {
@attackTable.Label Attack table key: @attackTable.Key
} @foreach (var criticalTable in referenceData.CriticalTables) { }
} @code { private LookupReferenceData? referenceData; private AttackLookupForm attackInput = new(); private CriticalLookupForm criticalInput = new(); private AttackLookupResponse? attackResult; private CriticalLookupResponse? criticalResult; private string? attackError; private string? criticalError; private CriticalTableReference? SelectedCriticalTable => referenceData?.CriticalTables.FirstOrDefault(table => table.Key == criticalInput.CriticalType); protected override async Task OnInitializedAsync() { referenceData = await LookupService.GetReferenceDataAsync(); attackInput.AttackTable = referenceData.AttackTables.FirstOrDefault()?.Key ?? string.Empty; attackInput.ArmorType = referenceData.ArmorTypes.FirstOrDefault()?.Key ?? string.Empty; var initialCriticalTable = referenceData.CriticalTables.FirstOrDefault(); criticalInput.CriticalType = initialCriticalTable?.Key ?? string.Empty; criticalInput.Column = initialCriticalTable?.Columns.FirstOrDefault()?.Key ?? string.Empty; criticalInput.Group = initialCriticalTable?.Groups.FirstOrDefault()?.Key ?? string.Empty; } private async Task RunAttackLookupAsync() { attackError = null; attackResult = null; if (!int.TryParse(attackInput.CriticalRollText, out var criticalRoll) && !string.IsNullOrWhiteSpace(attackInput.CriticalRollText)) { attackError = "Critical roll must be empty or a whole number."; return; } var response = await LookupService.LookupAttackAsync(new AttackLookupRequest( attackInput.AttackTable, attackInput.ArmorType, attackInput.AttackRoll, string.IsNullOrWhiteSpace(attackInput.CriticalRollText) ? null : criticalRoll)); if (response is null) { attackError = "No seeded attack result matched that table, armor type, and roll."; return; } attackResult = response; } private async Task RunCriticalLookupAsync() { criticalError = null; criticalResult = null; var response = await LookupService.LookupCriticalAsync(new CriticalLookupRequest( criticalInput.CriticalType, criticalInput.Column, criticalInput.Roll, SelectedCriticalTable?.Groups.Count > 0 ? criticalInput.Group : null)); if (response is null) { criticalError = "No loaded critical result matched that table, group, column, and roll."; return; } criticalResult = response; } private void HandleCriticalTableChanged(ChangeEventArgs args) { criticalInput.CriticalType = args.Value?.ToString() ?? string.Empty; var table = referenceData?.CriticalTables.FirstOrDefault(item => item.Key == criticalInput.CriticalType); criticalInput.Column = table?.Columns.FirstOrDefault()?.Key ?? string.Empty; criticalInput.Group = table?.Groups.FirstOrDefault()?.Key ?? string.Empty; criticalResult = null; criticalError = null; } private sealed class AttackLookupForm { public string AttackTable { get; set; } = string.Empty; public string ArmorType { get; set; } = string.Empty; public int AttackRoll { get; set; } = 66; public string? CriticalRollText { get; set; } = "72"; } private sealed class CriticalLookupForm { public string CriticalType { get; set; } = string.Empty; public string Column { get; set; } = string.Empty; public string Group { get; set; } = string.Empty; public int Roll { get; set; } = 72; } }