using System.Text.RegularExpressions; namespace IdleSpectator; /// /// Chronicle-voice templates. Hook = fact (); /// sentence = optional poetry (). Never invents facts. /// public static class LoreProse { private static readonly Regex KillPattern = new Regex(@"^Killed (.+) \(([^)]+)\)$", RegexOptions.CultureInvariant); private static readonly Regex KilledByPattern = new Regex( @"^(?:Killed by|Was cut down by|Was slain by) (.+) \(([^)]+)\)$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); private static readonly Regex EatenByPattern = new Regex( @"^(?:Eaten by|Was torn apart and eaten by|Devoured by) (.+)(?: \(([^)]+)\))?$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); private static readonly Regex LoversPattern = new Regex(@"^Became lovers with (.+)$", RegexOptions.CultureInvariant); private static readonly Regex FriendPattern = new Regex(@"^Befriended (.+)$", RegexOptions.CultureInvariant); private static readonly Regex WarVsPattern = new Regex(@"^War: (.+) vs (.+)$", RegexOptions.CultureInvariant); private static readonly Regex TotalWarPattern = new Regex(@"^Total war: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex KingdomFellPattern = new Regex(@"^Kingdom fell: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex KingdomNewPattern = new Regex(@"^New kingdom: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex CityDestroyedPattern = new Regex(@"^City destroyed: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex CityNewPattern = new Regex(@"^New city: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex NewKingPattern = new Regex(@"^New king: (.+) \((.+)\)$", RegexOptions.CultureInvariant); private static readonly Regex KingDiedPattern = new Regex(@"^King died: (.+) \((.+)\)$", RegexOptions.CultureInvariant); private static readonly Regex KingKilledPattern = new Regex(@"^King killed: (.+) \((.+)\)$", RegexOptions.CultureInvariant); private static readonly Regex AlliancePattern = new Regex(@"^Alliance: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex AllianceDissolvedPattern = new Regex(@"^Alliance dissolved: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex WarEndedPattern = new Regex(@"^War ended: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex RevoltPattern = new Regex(@"^Revolt: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex ExtinctPattern = new Regex(@"^Species extinct: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex FavoriteFellPattern = new Regex(@"^Favorite fell: (.+)$", RegexOptions.CultureInvariant); private static readonly Regex DisasterHint = new Regex(@"disaster", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); /// Rewrite a raw history/world fact line into short legendary prose. public static string Rewrite(ChronicleKind kind, string rawLine, string assetId = null) { if (string.IsNullOrEmpty(rawLine)) { return rawLine ?? ""; } // Long free-form harness / player lines stay as written. if (rawLine.Length > 96) { return rawLine; } switch (kind) { case ChronicleKind.Kill: return RewriteKill(rawLine); case ChronicleKind.Death: return RewriteDeath(rawLine); case ChronicleKind.Lover: return RewriteLovers(rawLine); case ChronicleKind.Friend: return RewriteFriend(rawLine); case ChronicleKind.World: return RewriteWorld(rawLine, assetId); default: return rawLine; } } private static string RewriteKill(string raw) { Match m = KillPattern.Match(raw); if (m.Success) { return "Slew " + m.Groups[1].Value + " of the " + m.Groups[2].Value + "s"; } return raw; } private static string RewriteDeath(string raw) { Match by = KilledByPattern.Match(raw); if (by.Success) { return "Fell at " + by.Groups[1].Value + "'s hand"; } Match eaten = EatenByPattern.Match(raw); if (eaten.Success) { return "Devoured by " + eaten.Groups[1].Value; } // New FormatDeathCause lines are already flavored - keep them. // Legacy strings still get a soft rewrite. switch (raw) { case "Died of old age": return "Faded gently into old age"; case "Starved to death": return "Wasted away from hunger"; case "Burned to death": return "Burned until only ash remained"; case "Burned in lava": return "Was swallowed by lava"; case "Drowned": return "Sank beneath the water and never rose"; case "Died of poison": return "Succumbed as poison flooded their veins"; case "Died of plague": return "Was claimed by a spreading plague"; case "Died of infection": return "Rotted from within as infection took hold"; case "Died of a tumor": return "Was consumed by a growing tumor"; case "Dissolved in acid": return "Dissolved screaming in acid"; case "Died of ash fever": return "Burned out from ash fever"; case "Fell to their death": return "Fell from a height and broke upon the ground"; case "Died in an explosion": return "Was torn apart in an explosion"; case "Struck down by divine power": return "Was smote by divine power"; case "Was eaten": return "Was torn apart and eaten"; case "Killed in combat": return "Fell in bitter combat"; case "Transformed away": return "Transformed away and left no body behind"; case "Died": return "Met their end"; default: return raw; } } private static string RewriteLovers(string raw) { Match m = LoversPattern.Match(raw); return m.Success ? "Heartbound to " + m.Groups[1].Value : raw; } private static string RewriteFriend(string raw) { Match m = FriendPattern.Match(raw); return m.Success ? "Bound in friendship with " + m.Groups[1].Value : raw; } private static string RewriteWorld(string raw, string assetId) { Match m; m = WarVsPattern.Match(raw); if (m.Success) { return "War kindled: " + m.Groups[1].Value + " and " + m.Groups[2].Value; } m = TotalWarPattern.Match(raw); if (m.Success) { return "Total war engulfed " + m.Groups[1].Value; } m = KingdomFellPattern.Match(raw); if (m.Success) { return "Kingdom fell: " + m.Groups[1].Value; } m = KingdomNewPattern.Match(raw); if (m.Success) { return "A kingdom rose: " + m.Groups[1].Value; } m = CityDestroyedPattern.Match(raw); if (m.Success) { return "City razed: " + m.Groups[1].Value; } m = CityNewPattern.Match(raw); if (m.Success) { return "City founded: " + m.Groups[1].Value; } m = NewKingPattern.Match(raw); if (m.Success) { return "Crowned: " + m.Groups[1].Value + " of " + m.Groups[2].Value; } m = KingDiedPattern.Match(raw); if (m.Success) { return "A king passed: " + m.Groups[1].Value + " of " + m.Groups[2].Value; } m = KingKilledPattern.Match(raw); if (m.Success) { return "A king slain: " + m.Groups[1].Value + " of " + m.Groups[2].Value; } m = AlliancePattern.Match(raw); if (m.Success) { return "Alliance forged: " + m.Groups[1].Value; } m = AllianceDissolvedPattern.Match(raw); if (m.Success) { return "Alliance broken: " + m.Groups[1].Value; } m = WarEndedPattern.Match(raw); if (m.Success) { return "War ended: " + m.Groups[1].Value; } m = RevoltPattern.Match(raw); if (m.Success) { return "Revolt in " + m.Groups[1].Value; } m = ExtinctPattern.Match(raw); if (m.Success) { return "Extinct: " + m.Groups[1].Value; } m = FavoriteFellPattern.Match(raw); if (m.Success) { return "A favorite fell: " + m.Groups[1].Value; } if (!string.IsNullOrEmpty(assetId) && DisasterHint.IsMatch(assetId)) { return "Disaster struck: " + raw; } if (!string.IsNullOrEmpty(assetId) && assetId.IndexOf("shattered", System.StringComparison.OrdinalIgnoreCase) >= 0) { return raw.Replace("Kingdom shattered:", "Kingdom shattered:"); } return raw; } }