Add phase 1 critical import tool

This commit is contained in:
2026-03-14 01:10:44 +01:00
parent 44af81cc38
commit f70d610c92
25 changed files with 851 additions and 166 deletions

View File

@@ -12,12 +12,12 @@ public static class RolemasterDbInitializer
await dbContext.Database.EnsureCreatedAsync(cancellationToken);
if (await dbContext.AttackTables.AnyAsync(cancellationToken) || await dbContext.CriticalTables.AnyAsync(cancellationToken))
if (await dbContext.AttackTables.AnyAsync(cancellationToken))
{
return;
}
RolemasterSeedData.Seed(dbContext);
RolemasterSeedData.SeedAttackStarterData(dbContext);
await dbContext.SaveChangesAsync(cancellationToken);
}
}

View File

@@ -4,17 +4,28 @@ namespace RolemasterDb.App.Data;
public static class RolemasterSeedData
{
public static void Seed(RolemasterDbContext dbContext)
public static void SeedAttackStarterData(RolemasterDbContext dbContext)
{
var armorTypes = CreateArmorTypes();
dbContext.ArmorTypes.AddRange(armorTypes);
List<ArmorType> armorTypes;
if (dbContext.ArmorTypes.Any())
{
armorTypes = dbContext.ArmorTypes.ToList();
}
else
{
armorTypes = CreateArmorTypes();
dbContext.ArmorTypes.AddRange(armorTypes);
}
if (dbContext.AttackTables.Any())
{
return;
}
var armorLookup = armorTypes.ToDictionary(item => item.Code, StringComparer.OrdinalIgnoreCase);
var attackTables = CreateAttackTables(armorLookup);
dbContext.AttackTables.AddRange(attackTables);
var criticalTables = CreateCriticalTables();
dbContext.CriticalTables.AddRange(criticalTables);
}
private static List<ArmorType> CreateArmorTypes() =>
@@ -131,164 +142,6 @@ public static class RolemasterSeedData
}
}
private static List<CriticalTable> CreateCriticalTables()
{
return
[
CreateCriticalTable(
slug: "slash",
displayName: "Slash Critical",
notes: "Starter subset derived from the critical-table schema design notes.",
matrix: new Dictionary<string, (string Description, string Affix)[]>
{
["A"] =
[
("Glancing slash across the forearm.", "+2 hits, 1 bleed"),
("Rib-level cut knocks the foe back a step.", "+4 hits, 1 stun, 2 bleed"),
("Deep cut to the thigh unbalances the target.", "+6 hits, 2 stun, 3 bleed"),
("Shoulder opened; guard collapses.", "+8 hits, 3 stun, 4 bleed"),
("Neck line opened. The fight is over.", "Instant death")
],
["B"] =
[
("Slash cuts through the hand and weapon grip wavers.", "+4 hits, 1 stun, 2 bleed"),
("Diagonal cut across the chest steals breath.", "+6 hits, 2 stun, 3 bleed"),
("Hip strike drives the foe to one knee.", "+8 hits, 3 stun, 4 bleed"),
("Sword arm carved deeply; weapon drops.", "+10 hits, 4 stun, 5 bleed"),
("Throat opened in a full red arc.", "Instant death")
],
["C"] =
[
("Forearm split to the bone; parry ruined.", "+6 hits, 2 stun, 3 bleed"),
("Wide cut over the ribs spins the foe.", "+8 hits, 3 stun, 4 bleed"),
("Hamstring sliced; prone unless magically supported.", "+10 hits, 4 stun, 6 bleed"),
("Slash rips across face and collar. Vision swims.", "+12 hits, 5 stun, 7 bleed"),
("Head nearly severed.", "Instant death")
],
["D"] =
[
("Deep abdominal slice spills momentum and blood together.", "+8 hits, 3 stun, 5 bleed"),
("Sword tracks from shoulder to sternum. Collapse likely.", "+10 hits, 4 stun, 6 bleed"),
("Leg cut through muscle; foe falls hard.", "+12 hits, 5 stun, 8 bleed"),
("Upper arm nearly removed; weapon arm useless.", "+14 hits, 6 stun, 10 bleed"),
("Killing cut through the neck.", "Instant death")
],
["E"] =
[
("Massive slash opens chest and the foe staggers blindly.", "+10 hits, 4 stun, 6 bleed"),
("Spine-grazing cut destroys posture and control.", "+12 hits, 5 stun, 8 bleed"),
("Leg severed below the knee.", "+14 hits, 6 stun, 10 bleed"),
("Torso opened from clavicle to hip.", "+16 hits, 8 stun, 12 bleed"),
("Clean decapitation.", "Instant death")
]
}),
CreateCriticalTable(
slug: "puncture",
displayName: "Puncture Critical",
notes: "Starter subset seeded for missile and thrust attacks.",
matrix: new Dictionary<string, (string Description, string Affix)[]>
{
["A"] =
[
("Point slips into the shoulder.", "+2 hits, 1 bleed"),
("Stab bites into the upper arm.", "+4 hits, 1 stun, 2 bleed"),
("Short thrust catches the ribs.", "+5 hits, 2 stun, 2 bleed"),
("Point pierces the thigh. Movement slows.", "+7 hits, 2 stun, 3 bleed"),
("Exact thrust to the throat.", "Instant death")
],
["B"] =
[
("Puncture through the palm forces a drop check.", "+4 hits, 1 stun, 2 bleed"),
("Arrow buries in the flank.", "+6 hits, 2 stun, 3 bleed"),
("Deep thrust into the belly doubles the foe over.", "+8 hits, 3 stun, 4 bleed"),
("Point through the shoulder locks the arm.", "+10 hits, 4 stun, 5 bleed"),
("Eye socket struck cleanly.", "Instant death")
],
["C"] =
[
("Stab through the bicep ruins the next parry.", "+6 hits, 2 stun, 3 bleed"),
("Arrow punches through lung tissue.", "+8 hits, 3 stun, 5 bleed"),
("Thrust under the ribs steals breath and footing.", "+10 hits, 4 stun, 6 bleed"),
("Deep puncture pins the foe in place.", "+12 hits, 5 stun, 7 bleed"),
("Heart strike.", "Instant death")
],
["D"] =
[
("Weapon point tears through the abdomen.", "+8 hits, 3 stun, 5 bleed"),
("Arrow lodges near the spine; movement nearly gone.", "+10 hits, 4 stun, 6 bleed"),
("Lance-like thrust through the torso.", "+12 hits, 5 stun, 8 bleed"),
("Point enters the neck and exits behind the shoulder.", "+14 hits, 6 stun, 10 bleed"),
("Brain pierced.", "Instant death")
],
["E"] =
[
("Massive puncture opens the chest cavity.", "+10 hits, 4 stun, 6 bleed"),
("Shaft buries to the fletching; foe drops.", "+12 hits, 5 stun, 8 bleed"),
("Groin-to-spine thrust destroys the body line.", "+14 hits, 6 stun, 10 bleed"),
("Perfect impalement through the throat and neck.", "+16 hits, 8 stun, 12 bleed"),
("Instantly fatal puncture.", "Instant death")
]
})
];
}
private static CriticalTable CreateCriticalTable(
string slug,
string displayName,
string notes,
IReadOnlyDictionary<string, (string Description, string Affix)[]> matrix)
{
var table = new CriticalTable
{
Slug = slug,
DisplayName = displayName,
Family = "standard",
SourceDocument = "Seeded starter data",
Notes = notes
};
table.Columns =
[
new CriticalColumn { ColumnKey = "A", Label = "A", SortOrder = 1 },
new CriticalColumn { ColumnKey = "B", Label = "B", SortOrder = 2 },
new CriticalColumn { ColumnKey = "C", Label = "C", SortOrder = 3 },
new CriticalColumn { ColumnKey = "D", Label = "D", SortOrder = 4 },
new CriticalColumn { ColumnKey = "E", Label = "E", SortOrder = 5 }
];
table.RollBands =
[
new CriticalRollBand { Label = "01-20", MinRoll = 1, MaxRoll = 20, SortOrder = 1 },
new CriticalRollBand { Label = "21-40", MinRoll = 21, MaxRoll = 40, SortOrder = 2 },
new CriticalRollBand { Label = "41-60", MinRoll = 41, MaxRoll = 60, SortOrder = 3 },
new CriticalRollBand { Label = "61-80", MinRoll = 61, MaxRoll = 80, SortOrder = 4 },
new CriticalRollBand { Label = "81-100", MinRoll = 81, MaxRoll = 100, SortOrder = 5 }
];
var columnsByKey = table.Columns.ToDictionary(item => item.ColumnKey, StringComparer.OrdinalIgnoreCase);
var bandsByOrder = table.RollBands.ToDictionary(item => item.SortOrder);
foreach (var (columnKey, rows) in matrix)
{
for (var index = 0; index < rows.Length; index++)
{
var row = rows[index];
table.Results.Add(new CriticalResult
{
CriticalColumn = columnsByKey[columnKey],
CriticalRollBand = bandsByOrder[index + 1],
DescriptionText = row.Description,
RawAffixText = row.Affix,
RawCellText = $"{row.Description} {row.Affix}",
ParsedJson = "{}",
ParseStatus = "verified"
});
}
}
return table;
}
private static string BuildAttackNotation(int hits, string? severity, string criticalType)
{
return severity is null

Binary file not shown.