Add quick parse notation for critical editor

This commit is contained in:
2026-03-15 12:58:26 +01:00
parent d1ad95e861
commit 7b07477133
12 changed files with 666 additions and 35 deletions

View File

@@ -9,6 +9,14 @@ public static class AffixEffectParser
private static readonly Regex DirectHitsRegex = new(@"[+-]\s*\d+\s*H\b", RegexOptions.Compiled);
private static readonly Regex PowerPointModifierRegex = new(@"\+\s*\((?<expression>[^)]+)\)\s*P\b", RegexOptions.Compiled);
private static readonly Regex ModifierRegex = new(@"\((?<noise>[^0-9+\-)]*)(?<sign>[+-])\s*(?<value>\d+)\)", RegexOptions.Compiled);
private static readonly Regex QuickDirectHitsRegex = new(@"^\+(?<value>\d+)(?:H)?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex QuickStunnedRegex = new(@"^(?<value>\d+)s$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex QuickMustParryRegex = new(@"^(?<value>\d+)mp$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex QuickNoParryRegex = new(@"^(?<value>\d+)np$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex QuickBleedRegex = new(@"^(?<value>\d+)hpr$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex QuickFoePenaltyRegex = new(@"^-(?<value>\d+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex QuickAttackerBonusRegex = new(@"^\+(?<value>\d+)b$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex QuickPowerPointRegex = new(@"^\+(?<expression>.+)pp$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static IReadOnlyList<ParsedCriticalEffect> Parse(string? rawAffixText, AffixLegend affixLegend)
{
@@ -27,6 +35,31 @@ public static class AffixEffectParser
return effects;
}
public static bool TryParseAffixToken(
string token,
AffixLegend affixLegend,
out IReadOnlyList<ParsedCriticalEffect> effects,
out string normalizedToken)
{
normalizedToken = CriticalCellParserSupport.CollapseWhitespace(token);
if (TryParseQuickToken(normalizedToken, out var quickEffects, out var canonicalToken))
{
normalizedToken = canonicalToken;
effects = quickEffects;
return true;
}
var legacyEffects = Parse(normalizedToken, affixLegend);
if (legacyEffects.Count > 0)
{
effects = legacyEffects;
return true;
}
effects = [];
return false;
}
private static void ParseLine(string line, AffixLegend affixLegend, List<ParsedCriticalEffect> effects)
{
if (string.IsNullOrWhiteSpace(line) || line is "-" or "" or "—")
@@ -163,6 +196,174 @@ public static class AffixEffectParser
}
}
private static bool TryParseQuickToken(
string token,
out IReadOnlyList<ParsedCriticalEffect> effects,
out string canonicalToken)
{
canonicalToken = token;
if (TryMatchSingle(QuickDirectHitsRegex, token, match =>
new ParsedCriticalEffect(
CriticalEffectCodes.DirectHits,
FoeTarget,
int.Parse(match.Groups["value"].Value),
null,
null,
null,
null,
null,
false,
"quick",
$"+{match.Groups["value"].Value}"), out effects, out canonicalToken))
{
return true;
}
if (TryMatchSingle(QuickStunnedRegex, token, match =>
new ParsedCriticalEffect(
CriticalEffectCodes.StunnedRounds,
FoeTarget,
null,
null,
int.Parse(match.Groups["value"].Value),
null,
null,
null,
false,
"quick",
$"{match.Groups["value"].Value}s"), out effects, out canonicalToken))
{
return true;
}
if (TryMatchSingle(QuickMustParryRegex, token, match =>
new ParsedCriticalEffect(
CriticalEffectCodes.MustParryRounds,
FoeTarget,
null,
null,
int.Parse(match.Groups["value"].Value),
null,
null,
null,
false,
"quick",
$"{match.Groups["value"].Value}mp"), out effects, out canonicalToken))
{
return true;
}
if (TryMatchSingle(QuickNoParryRegex, token, match =>
new ParsedCriticalEffect(
CriticalEffectCodes.NoParryRounds,
FoeTarget,
null,
null,
int.Parse(match.Groups["value"].Value),
null,
null,
null,
false,
"quick",
$"{match.Groups["value"].Value}np"), out effects, out canonicalToken))
{
return true;
}
if (TryMatchSingle(QuickBleedRegex, token, match =>
new ParsedCriticalEffect(
CriticalEffectCodes.BleedPerRound,
FoeTarget,
null,
null,
null,
int.Parse(match.Groups["value"].Value),
null,
null,
false,
"quick",
$"{match.Groups["value"].Value}hpr"), out effects, out canonicalToken))
{
return true;
}
if (TryMatchSingle(QuickFoePenaltyRegex, token, match =>
new ParsedCriticalEffect(
CriticalEffectCodes.FoePenalty,
FoeTarget,
null,
null,
null,
null,
-int.Parse(match.Groups["value"].Value),
null,
false,
"quick",
$"-{match.Groups["value"].Value}"), out effects, out canonicalToken))
{
return true;
}
if (TryMatchSingle(QuickAttackerBonusRegex, token, match =>
new ParsedCriticalEffect(
CriticalEffectCodes.AttackerBonusNextRound,
null,
null,
null,
null,
null,
int.Parse(match.Groups["value"].Value),
null,
false,
"quick",
$"+{match.Groups["value"].Value}b"), out effects, out canonicalToken))
{
return true;
}
if (TryMatchSingle(QuickPowerPointRegex, token, match =>
new ParsedCriticalEffect(
CriticalEffectCodes.PowerPointModifier,
FoeTarget,
null,
NormalizePowerPointExpression(match.Groups["expression"].Value),
null,
null,
null,
null,
false,
"quick",
$"+{NormalizePowerPointExpression(match.Groups["expression"].Value)}pp"), out effects, out canonicalToken))
{
return true;
}
effects = [];
return false;
}
private static bool TryMatchSingle(
Regex regex,
string token,
Func<Match, ParsedCriticalEffect> createEffect,
out IReadOnlyList<ParsedCriticalEffect> effects,
out string canonicalToken)
{
var match = regex.Match(token);
if (!match.Success)
{
effects = [];
canonicalToken = token;
return false;
}
var effect = createEffect(match);
effects = [effect];
canonicalToken = effect.SourceText ?? token;
return true;
}
private static ParsedCriticalEffect CreateSymbolEffect(string effectCode, int magnitude, string sourceText) =>
effectCode switch
{
@@ -276,4 +477,22 @@ public static class AffixEffectParser
.Replace(" +", "+", StringComparison.Ordinal)
.Replace("( ", "(", StringComparison.Ordinal)
.Replace(" )", ")", StringComparison.Ordinal);
private static string NormalizePowerPointExpression(string value)
{
var normalized = CriticalCellParserSupport.CollapseWhitespace(value)
.Replace(" ", string.Empty, StringComparison.Ordinal);
if (normalized.StartsWith('+'))
{
normalized = normalized[1..];
}
if (normalized.StartsWith('(') && normalized.EndsWith(')') && normalized.Length > 2)
{
normalized = normalized[1..^1];
}
return normalized;
}
}