diff --git a/ImperatorToCK3/CK3/CK3LocDB.cs b/ImperatorToCK3/CK3/CK3LocDB.cs index a3cc8dba4..aad191958 100644 --- a/ImperatorToCK3/CK3/CK3LocDB.cs +++ b/ImperatorToCK3/CK3/CK3LocDB.cs @@ -6,7 +6,7 @@ using Murmur; using System.Collections.Generic; using System.IO; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.CK3; @@ -23,7 +23,7 @@ public void LoadLocFromModFS(ModFilesystem ck3ModFS, IEnumerable activeM modFSLocDB.ScrapeLocalizations(ck3ModFS); ImportLocFromLocDB(modFSLocDB); - // Read loc from ImperatorToCK3 congifurables. + // Read loc from ImperatorToCK3 configurables. // It will only be outputted for keys localized in neither ModFSLocDB nor ConverterGeneratedLocDB. LoadOptionalLoc(activeModFlags); } @@ -54,7 +54,8 @@ private void LoadOptionalLoc(IEnumerable activeModFlags) { if (!Directory.Exists(modLocDir)) { continue; } - optionalLocFilePaths = optionalLocFilePaths.Concat(Directory.GetFiles(modLocDir, "*.yml", SearchOption.AllDirectories)).ToArray(); + optionalLocFilePaths = optionalLocFilePaths.AsValueEnumerable() + .Concat(Directory.GetFiles(modLocDir, "*.yml", SearchOption.AllDirectories)).ToArray(); } var optionalConverterLocDB = new LocDB(ConverterGlobals.PrimaryLanguage, ConverterGlobals.SecondaryLanguages); @@ -161,7 +162,13 @@ public bool KeyHasConflictingHash(string key) { private static string GetHashStrForKey(string key) { var keyBytes = System.Text.Encoding.UTF8.GetBytes(key); var hash = murmur3A.ComputeHash(keyBytes); - return string.Concat(hash.Select(b => b.ToString("X2"))); + + var sb = new System.Text.StringBuilder(hash.Length * 2); + foreach (byte t in hash) { + sb.Append(t.ToString("X2")); + } + + return sb.ToString(); } private readonly Dictionary hashToKeyDict = []; // stores MurmurHash3A hash to key mapping diff --git a/ImperatorToCK3/CK3/Characters/Character.cs b/ImperatorToCK3/CK3/Characters/Character.cs index 930ef1222..9197e7477 100644 --- a/ImperatorToCK3/CK3/Characters/Character.cs +++ b/ImperatorToCK3/CK3/Characters/Character.cs @@ -17,11 +17,10 @@ using ImperatorToCK3.Mappers.UnitType; using Open.Collections; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using System.Text; +using ZLinq; -namespace ImperatorToCK3.CK3.Characters; +namespace ImperatorToCK3.CK3.Characters; internal sealed class Character : IIdentifiable { public string Id { get; } @@ -37,7 +36,7 @@ public bool Female { return false; } - var value = entries.LastOrDefault().Value; + var value = entries.AsValueEnumerable().LastOrDefault().Value; if (value is string str) { return str == "yes"; } @@ -66,11 +65,12 @@ public void SetNickname(string nickname, Date? date) { public string? GetNickname(Date date) { return History.GetFieldValue("give_nickname", date)?.ToString(); } - - public IEnumerable BaseTraits => History.Fields["traits"].InitialEntries + + public List BaseTraits => History.Fields["traits"].InitialEntries.AsValueEnumerable() .Where(kvp => kvp.Key == "trait") .Select(kvp => kvp.Value) - .Cast(); + .Cast() + .ToList(); public void AddBaseTrait(string traitId) { History.Fields["traits"].InitialEntries.Add(new KeyValuePair("trait", traitId)); @@ -94,7 +94,7 @@ public string GetAgeSex(Date date) { } public Date BirthDate { - get => History.Fields["birth"].DateToEntriesDict.First().Key; + get => History.Fields["birth"].DateToEntriesDict.AsValueEnumerable().First().Key; set { var field = History.Fields["birth"]; field.RemoveAllEntries(); @@ -105,7 +105,7 @@ public Date BirthDate { public Date? DeathDate { get { var entriesDict = History.Fields["death"].DateToEntriesDict; - return entriesDict.Count == 0 ? null : entriesDict.First().Key; + return entriesDict.Count == 0 ? null : entriesDict.AsValueEnumerable().First().Key; } set { var field = History.Fields["death"]; @@ -121,7 +121,7 @@ public string? DeathReason { if (entriesDict.Count == 0) { return null; } - var deathObj = entriesDict.First().Value[^1].Value; + var deathObj = entriesDict.AsValueEnumerable().First().Value[^1].Value; if (deathObj is not StringOfItem deathStrOfItem || !deathStrOfItem.IsArrayOrObject()) { return null; } @@ -142,7 +142,7 @@ public string? DeathReason { } // Modify the last entry in the history to include the death reason. - var entriesList = entriesDict.First().Value; + var entriesList = entriesDict.AsValueEnumerable().First().Value; var lastEntry = entriesList[^1]; // No reason provided. var deathStr = value is null ? "yes" : $"{{ death_reason = {value} }}"; @@ -226,7 +226,7 @@ public string? DeathReason { public void InitSpousesCache() { var spousesHistoryField = History.Fields["spouses"]; - foreach (var spouseId in spousesHistoryField.InitialEntries.Select(kvp => kvp.Value.ToString())) { + foreach (var spouseId in spousesHistoryField.InitialEntries.AsValueEnumerable().Select(kvp => kvp.Value.ToString())) { if (spouseId is null) { continue; } @@ -251,7 +251,7 @@ public void InitSpousesCache() { public void InitConcubinesCache() { var concubinesHistoryField = History.Fields["concubines"]; - foreach (var concubineId in concubinesHistoryField.InitialEntries.Select(kvp => kvp.Value.ToString())) { + foreach (var concubineId in concubinesHistoryField.InitialEntries.AsValueEnumerable().Select(kvp => kvp.Value.ToString())) { if (concubineId is null) { continue; } @@ -423,7 +423,7 @@ ISet unlocalizedImperatorNames irProvIdForProvMapper = ImperatorCharacter.Mother.ProvinceId; } if (IsImperatorProvIdInvalidForCharacterSource(irProvIdForProvMapper, provinceMapper) && ImperatorCharacter.Spouses.Count > 0) { - var firstSpouse = ImperatorCharacter.Spouses.First().Value; + var firstSpouse = ImperatorCharacter.Spouses.AsValueEnumerable().First().Value; irProvIdForProvMapper = firstSpouse.ProvinceId; } @@ -579,11 +579,11 @@ public void RemoveAllConcubines() { public void RemoveAllChildren() { if (Female) { - foreach (var child in childrenCache.Where(c => c.MotherId == Id)) { + foreach (var child in childrenCache.AsValueEnumerable().Where(c => c.MotherId == Id)) { child.Mother = null; } } else { - foreach (var child in childrenCache.Where(c => c.FatherId == Id)) { + foreach (var child in childrenCache.AsValueEnumerable().Where(c => c.FatherId == Id)) { child.Father = null; } } @@ -690,7 +690,7 @@ public Character? Father { } } - public IReadOnlyCollection Children => characters + public IReadOnlyCollection Children => characters.AsValueEnumerable() .Where(c => c.FatherId == Id || c.MotherId == Id) .ToImmutableList(); @@ -802,7 +802,7 @@ CK3LocDB ck3LocDB sb.AppendLine($"\t\t\tmen_at_arms={{type={type} men={men}}}"); } - var ck3Location = provinceMapper.GetCK3ProvinceNumbers(unit.Location) + var ck3Location = provinceMapper.GetCK3ProvinceNumbers(unit.Location).AsValueEnumerable() .Cast() .FirstOrDefault(defaultValue: null); if (ck3Location is not null) { diff --git a/ImperatorToCK3/CK3/Characters/CharacterCollection.cs b/ImperatorToCK3/CK3/Characters/CharacterCollection.cs index 5cc8634b7..932ef47c2 100644 --- a/ImperatorToCK3/CK3/Characters/CharacterCollection.cs +++ b/ImperatorToCK3/CK3/Characters/CharacterCollection.cs @@ -19,6 +19,7 @@ using Open.Collections; using System; using System.Collections.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; @@ -266,8 +267,8 @@ Date GetEstimatedMarriageDate(Imperator.Characters.Character imperatorCharacter, return conversionDate; } Date? GetBirthDateOfFirstCommonChild(Imperator.Characters.Character father, Imperator.Characters.Character mother) { - var childrenOfFather = father.Children.Values.ToHashSet(); - var childrenOfMother = mother.Children.Values.ToHashSet(); + var childrenOfFather = father.Children.Values.ToFrozenSet(); + var childrenOfMother = mother.Children.Values.ToFrozenSet(); var commonChildren = childrenOfFather.Intersect(childrenOfMother).OrderBy(child => child.BirthDate).ToArray(); Date? firstChildBirthDate = commonChildren.Length > 0 ? commonChildren.FirstOrDefault()?.BirthDate : null; @@ -386,7 +387,7 @@ private void SetCharacterCastes(CultureCollection cultures, Date ck3BookmarkDate var casteSystemCultureIds = cultures .Where(c => c.TraditionIds.Contains("tradition_caste_system")) .Select(c => c.Id) - .ToHashSet(); + .ToFrozenSet(); var learningEducationTraits = new[]{"education_learning_1", "education_learning_2", "education_learning_3", "education_learning_4"}; foreach (var character in this.OrderBy(c => c.BirthDate)) { @@ -418,14 +419,14 @@ private void SetCharacterCastes(CultureCollection cultures, Date ck3BookmarkDate } // Try to set caste based on character's traits. - var traitIds = character.BaseTraits.ToHashSet(); + var traitIds = character.BaseTraits.ToFrozenSet(); character.AddBaseTrait(traitIds.Intersect(learningEducationTraits).Any() ? "brahmin" : "kshatriya"); } return; static string? GetCasteTraitFromParent(Character parentCharacter) { var casteTraits = new[]{"brahmin", "kshatriya", "vaishya", "shudra"}; - var parentTraitIds = parentCharacter.BaseTraits.ToHashSet(); + var parentTraitIds = parentCharacter.BaseTraits.ToFrozenSet(); return casteTraits.Intersect(parentTraitIds).FirstOrDefault(); } } @@ -503,7 +504,7 @@ public void PurgeUnneededCharacters(Title.LandedTitles titles, DynastyCollection .Select(character => character.GetDynastyId(ck3BookmarkDate)) .Distinct() .Where(id => id is not null) - .ToHashSet(); + .ToFrozenSet(); var i = 0; var charactersToRemove = new List(); @@ -595,7 +596,7 @@ static void AddGoldToCharacter(Character character, double gold) { var vassalCharacterIds = ck3Country.GetDeFactoVassals(bookmarkDate).Values .Where(vassalTitle => !vassalTitle.Landless) .Select(vassalTitle => vassalTitle.GetHolderId(bookmarkDate)) - .ToHashSet(); + .ToFrozenSet(); var vassalCharacters = new HashSet(); foreach (var vassalCharacterId in vassalCharacterIds) { @@ -830,7 +831,7 @@ internal void ConvertImperatorCharacterDNA(DNAFactory dnaFactory) { public void RemoveUndefinedTraits(TraitMapper traitMapper) { Logger.Info("Removing undefined traits from CK3 character history..."); - var definedTraits = traitMapper.ValidCK3TraitIDs.ToHashSet(); + var definedTraits = traitMapper.ValidCK3TraitIDs.ToFrozenSet(); foreach (var character in this) { if (character.FromImperator) { @@ -849,7 +850,7 @@ public void RemoveInvalidDynastiesFromHistory(DynastyCollection dynasties) { Logger.Info("Removing invalid dynasties from CK3 character history..."); var ck3Characters = this.Where(c => !c.FromImperator).ToArray(); - var validDynastyIds = dynasties.Select(d => d.Id).ToHashSet(); + var validDynastyIds = dynasties.Select(d => d.Id).ToFrozenSet(); foreach (var character in ck3Characters) { if (!character.History.Fields.TryGetValue("dynasty", out var dynastyField)) { diff --git a/ImperatorToCK3/CK3/Characters/CharactersLoader.cs b/ImperatorToCK3/CK3/Characters/CharactersLoader.cs index b0e7bd51b..d5de1a0ed 100644 --- a/ImperatorToCK3/CK3/Characters/CharactersLoader.cs +++ b/ImperatorToCK3/CK3/Characters/CharactersLoader.cs @@ -1,8 +1,9 @@ using commonItems; using commonItems.Mods; using Open.Collections.Synchronized; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.CK3.Characters; @@ -51,8 +52,10 @@ public void LoadCK3Characters(ModFilesystem ck3ModFS, Date bookmarkDate) { "claims", ]; - var femaleCharacterIds = loadedCharacters.Where(c => c.Female).Select(c => c.Id).ToHashSet(); - var maleCharacterIds = loadedCharacters.Select(c => c.Id).Except(femaleCharacterIds).ToHashSet(); + var femaleCharacterIds = loadedCharacters.AsValueEnumerable() + .Where(c => c.Female).Select(c => c.Id).ToFrozenSet(); + var maleCharacterIds = loadedCharacters.AsValueEnumerable() + .Select(c => c.Id).Except(femaleCharacterIds).ToFrozenSet(); foreach (var character in loadedCharacters) { // Clear some fields we don't need. @@ -91,7 +94,8 @@ public void LoadCK3Characters(ModFilesystem ck3ModFS, Date bookmarkDate) { // Remove effects that set relations. They don't matter a lot in our alternate timeline. character.History.Fields["effects"].RemoveAllEntries( - entry => irrelevantEffects.Any(effect => entry.ToString()?.Contains(effect) ?? false)); + entry => irrelevantEffects.AsValueEnumerable() + .Any(effect => entry.ToString()?.Contains(effect) ?? false)); // Fix characters being set as their own fathers/mothers. if (character.FatherId == character.Id) { @@ -111,7 +115,7 @@ public void LoadCK3Characters(ModFilesystem ck3ModFS, Date bookmarkDate) { Logger.Info("Loaded CK3 characters."); } - private static void RemoveInvalidMotherAndFatherEntries(Character character, HashSet femaleCharacterIds, HashSet maleCharacterIds) { + private static void RemoveInvalidMotherAndFatherEntries(Character character, FrozenSet femaleCharacterIds, FrozenSet maleCharacterIds) { // Remove wrong sex mother and father references (male mothers, female fathers). var motherField = character.History.Fields["mother"]; motherField.RemoveAllEntries(value => { diff --git a/ImperatorToCK3/CK3/Cultures/Culture.cs b/ImperatorToCK3/CK3/Cultures/Culture.cs index 52aac22fa..0bae14a94 100644 --- a/ImperatorToCK3/CK3/Cultures/Culture.cs +++ b/ImperatorToCK3/CK3/Cultures/Culture.cs @@ -4,6 +4,7 @@ using commonItems.Serialization; using ImperatorToCK3.Mappers.Technology; using Open.Collections; +using System.Collections.Frozen; using System.Collections.Generic; using System.IO; using System.Linq; @@ -103,7 +104,7 @@ public async Task OutputHistory(string outputModPath, Date date) { await historyWriter.WriteAsync(historyStrBuilder.ToString()); } - public void ImportInnovationsFromImperator(ISet irInventions, InnovationMapper innovationMapper) { + public void ImportInnovationsFromImperator(FrozenSet irInventions, InnovationMapper innovationMapper) { innovationsFromImperator.AddRange(innovationMapper.GetInnovations(irInventions)); var progresses = innovationMapper.GetInnovationProgresses(irInventions); diff --git a/ImperatorToCK3/CK3/Cultures/CultureCollection.cs b/ImperatorToCK3/CK3/Cultures/CultureCollection.cs index a868d55ed..87786ad5b 100644 --- a/ImperatorToCK3/CK3/Cultures/CultureCollection.cs +++ b/ImperatorToCK3/CK3/Cultures/CultureCollection.cs @@ -11,6 +11,7 @@ using ImperatorToCK3.Mappers.Province; using ImperatorToCK3.Mappers.Technology; using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; @@ -249,7 +250,7 @@ public void LoadInnovationIds(ModFilesystem ck3ModFS) { var irInventions = grouping .SelectMany(c => c.Country.GetActiveInventionIds(inventionsDB)) - .ToHashSet(); + .ToFrozenSet(); culture.ImportInnovationsFromImperator(irInventions, innovationMapper); } } diff --git a/ImperatorToCK3/CK3/Cultures/PillarCollection.cs b/ImperatorToCK3/CK3/Cultures/PillarCollection.cs index 737da78f2..4188e0aaa 100644 --- a/ImperatorToCK3/CK3/Cultures/PillarCollection.cs +++ b/ImperatorToCK3/CK3/Cultures/PillarCollection.cs @@ -4,10 +4,11 @@ using commonItems.Mods; using ImperatorToCK3.CommonUtils; using System; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Linq; +using ZLinq; -namespace ImperatorToCK3.CK3.Cultures; +namespace ImperatorToCK3.CK3.Cultures; internal sealed class PillarCollection : IdObjectCollection { private readonly Dictionary mergedPillarsDict = []; @@ -17,21 +18,21 @@ public PillarCollection(ColorFactory colorFactory, OrderedDictionary p.Type == "heritage").ToHashSet(); + var heritages = this.AsValueEnumerable().Where(p => p.Type == "heritage").ToFrozenSet(); if (mergedPillarsDict.TryGetValue(heritageId, out var mergedHeritageId)) { - return heritages.FirstOrDefault(p => p.Id == mergedHeritageId); + return heritages.AsValueEnumerable().FirstOrDefault(p => p.Id == mergedHeritageId); } - return heritages.FirstOrDefault(p => p.Id == heritageId); + return heritages.AsValueEnumerable().FirstOrDefault(p => p.Id == heritageId); } public Pillar? GetLanguageForId(string languageId) { - var languages = this.Where(p => p.Type == "language").ToHashSet(); + var languages = this.AsValueEnumerable().Where(p => p.Type == "language").ToFrozenSet(); if (mergedPillarsDict.TryGetValue(languageId, out var mergedLanguageId)) { - return languages.FirstOrDefault(p => p.Id == mergedLanguageId); + return languages.AsValueEnumerable().FirstOrDefault(p => p.Id == mergedLanguageId); } - return languages.FirstOrDefault(p => p.Id == languageId); + return languages.AsValueEnumerable().FirstOrDefault(p => p.Id == languageId); } public void LoadPillars(ModFilesystem ck3ModFS, OrderedDictionary ck3ModFlags) { @@ -55,7 +56,7 @@ private void LoadPillar(string pillarId, BufferedReader pillarReader, OrderedDic pillarDataParser.ParseStream(pillarReader); - if (pillarData.InvalidatingPillarIds.Any()) { + if (pillarData.InvalidatingPillarIds.AsValueEnumerable().Any()) { foreach (var existingPillar in this) { if (!pillarData.InvalidatingPillarIds.Contains(existingPillar.Id)) { continue; @@ -78,27 +79,27 @@ private void LoadPillar(string pillarId, BufferedReader pillarReader, OrderedDic // Perform some non-breaking validation. if (pillar.Type == "heritage") { if (ck3ModFlags["wtwsms"] || ck3ModFlags["tfe"] || ck3ModFlags["roa"]) { - if (!pillar.Parameters.Any(p => p.Key.StartsWith("heritage_family_"))) { + if (!pillar.Parameters.AsValueEnumerable().Any(p => p.Key.StartsWith("heritage_family_"))) { Logger.Warn($"Heritage {pillarId} is missing required heritage_family parameter!"); } - if (!pillar.Parameters.Any(p => p.Key.StartsWith("heritage_group_"))) { + if (!pillar.Parameters.AsValueEnumerable().Any(p => p.Key.StartsWith("heritage_group_"))) { Logger.Warn($"Heritage {pillarId} is missing required heritage_group parameter!"); } } } if (pillar.Type == "language") { if (ck3ModFlags["wtwsms"] || ck3ModFlags["tfe"] || ck3ModFlags["roa"]) { - if (!pillar.Parameters.Any(p => p.Key.StartsWith("language_family_"))) { + if (!pillar.Parameters.AsValueEnumerable().Any(p => p.Key.StartsWith("language_family_"))) { Logger.Warn($"Language {pillarId} is missing required language_family parameter!"); } } if (ck3ModFlags["wtwsms"]) { - if (!pillar.Parameters.Any(p => p.Key.StartsWith("language_branch_"))) { + if (!pillar.Parameters.AsValueEnumerable().Any(p => p.Key.StartsWith("language_branch_"))) { Logger.Warn($"Language {pillarId} is missing required language_branch parameter!"); } } if (ck3ModFlags["tfe"] || ck3ModFlags["roa"]) { - if (!pillar.Parameters.Any(p => p.Key.StartsWith("language_group_"))) { + if (!pillar.Parameters.AsValueEnumerable().Any(p => p.Key.StartsWith("language_group_"))) { Logger.Warn($"Language {pillarId} is missing required language_group parameter!"); } } @@ -135,7 +136,7 @@ private void LoadInvalidatingPillarIds(OrderedDictionary ck3ModFla pillarData.InvalidatingPillarIds = modPillarIdsReader.GetStrings(); }); } else { - foreach (var modFlag in ck3ModFlags.Where(f => f.Value)) { + foreach (var modFlag in ck3ModFlags.AsValueEnumerable().Where(f => f.Value)) { pillarIdsPerModFlagParser.RegisterKeyword(modFlag.Key, modPillarIdsReader => { pillarData.InvalidatingPillarIds = modPillarIdsReader.GetStrings(); }); diff --git a/ImperatorToCK3/CK3/Dynasties/Dynasty.cs b/ImperatorToCK3/CK3/Dynasties/Dynasty.cs index 006b1883d..79e333c21 100644 --- a/ImperatorToCK3/CK3/Dynasties/Dynasty.cs +++ b/ImperatorToCK3/CK3/Dynasties/Dynasty.cs @@ -8,7 +8,7 @@ using ImperatorToCK3.Imperator.Families; using ImperatorToCK3.Mappers.Culture; using System.Diagnostics.CodeAnalysis; -using System.Linq; +using ZLinq; using ImperatorCharacter = ImperatorToCK3.Imperator.Characters.Character; @@ -22,7 +22,7 @@ public Dynasty(Family irFamily, CharacterCollection irCharacters, CulturesDB irC Name = Id; var imperatorMemberIds = irFamily.MemberIds; - var imperatorMembers = irCharacters + var imperatorMembers = irCharacters.AsValueEnumerable() .Where(c => imperatorMemberIds.Contains(c.Id)) .ToArray(); @@ -84,7 +84,7 @@ public Dynasty(string dynastyId, BufferedReader dynastyReader) { public string NameForSerialization { get { // If the name contains whitespace, it needs to be quoted. - return Name.Any(char.IsWhiteSpace) ? $"\"{Name}\"" : Name; + return Name.AsValueEnumerable().Any(char.IsWhiteSpace) ? $"\"{Name}\"" : Name; } } [NonSerialized] public string Name { get; private set; } @@ -104,7 +104,7 @@ private void SetCultureFromImperator(Family irFamily, ImperatorCharacter[] irMem } // Try to set culture from other members. - var otherImperatorMembers = irMembers.Skip(1).ToArray(); + var otherImperatorMembers = irMembers.AsValueEnumerable().Skip(1).ToArray(); foreach (var otherImperatorMember in otherImperatorMembers) { if (otherImperatorMember.CK3Character is null) { continue; @@ -117,10 +117,10 @@ private void SetCultureFromImperator(Family irFamily, ImperatorCharacter[] irMem // Try to set culture from family. var irCultureId = irFamily.Culture; - var irProvinceIdForMapping = irMembers + var irProvinceIdForMapping = irMembers.AsValueEnumerable() .Select(m => m.ProvinceId) .FirstOrDefault(id => id.HasValue); - var countryTag = irMembers + var countryTag = irMembers.AsValueEnumerable() .Select(m => m.Country?.HistoricalTag) .FirstOrDefault(tag => tag is not null, defaultValue: null); var ck3CultureId = cultureMapper.Match(irCultureId, null, irProvinceIdForMapping, countryTag); diff --git a/ImperatorToCK3/CK3/Dynasties/HouseCollection.cs b/ImperatorToCK3/CK3/Dynasties/HouseCollection.cs index 52f44fcc5..4f0173fbd 100644 --- a/ImperatorToCK3/CK3/Dynasties/HouseCollection.cs +++ b/ImperatorToCK3/CK3/Dynasties/HouseCollection.cs @@ -2,8 +2,9 @@ using commonItems.Collections; using commonItems.Mods; using ImperatorToCK3.CK3.Characters; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.CK3.Dynasties; @@ -32,15 +33,15 @@ public void PurgeUnneededHouses(CharacterCollection ck3Characters, Date date) { nonRemovableIdsParser.IgnoreAndLogUnregisteredItems(); nonRemovableIdsParser.ParseFile("configurables/dynasty_houses_to_preserve.txt"); - HashSet houseIdsToKeep = ck3Characters + FrozenSet houseIdsToKeep = ck3Characters.AsValueEnumerable() .Select(c => c.GetDynastyHouseId(date)) .Where(id => id is not null) .Distinct() .Cast() - .ToHashSet(); + .ToFrozenSet(); int removedCount = 0; - foreach (var house in this.ToArray()) { + foreach (var house in this.AsValueEnumerable().ToArray()) { if (houseIdsToKeep.Contains(house.Id)) { continue; } diff --git a/ImperatorToCK3/CK3/ParserExtensions.cs b/ImperatorToCK3/CK3/ParserExtensions.cs index 30973b1e5..f08d92ed1 100644 --- a/ImperatorToCK3/CK3/ParserExtensions.cs +++ b/ImperatorToCK3/CK3/ParserExtensions.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.CK3; @@ -91,7 +91,7 @@ public static void RegisterModDependentBloc(this Parser parser, IDictionary ck3ModFlags) { // The file used the Liquid templating language, so convert it to text before parsing. - var convertedModFlags = ck3ModFlags.ToDictionary(kv => kv.Key, kv => (object)kv.Value); + var convertedModFlags = ck3ModFlags.AsValueEnumerable().ToDictionary(kv => kv.Key, kv => (object)kv.Value); var context = Hash.FromDictionary(convertedModFlags); var liquidText = File.ReadAllText(filePath); @@ -104,10 +104,10 @@ public static void ParseLiquidFile(this Parser parser, string filePath, IDiction public static void ParseFolderWithLiquidSupport(this Parser parser, string path, string extensions, bool recursive, IDictionary ck3ModFlags, bool logFilePaths = false) { var searchPattern = recursive ? "*" : "*.*"; var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - var files = Directory.GetFiles(path, searchPattern, searchOption).ToList(); + var files = Directory.GetFiles(path, searchPattern, searchOption).AsValueEnumerable().ToList(); var validExtensions = extensions.Split(';'); - files.RemoveWhere(f => !validExtensions.Contains(CommonFunctions.GetExtension(f))); + files.RemoveWhere(f => !validExtensions.AsValueEnumerable().Contains(CommonFunctions.GetExtension(f))); foreach (var file in files) { if (logFilePaths) { diff --git a/ImperatorToCK3/CK3/Provinces/ProvinceCollection.cs b/ImperatorToCK3/CK3/Provinces/ProvinceCollection.cs index 18c8ef4a8..59fa3490c 100644 --- a/ImperatorToCK3/CK3/Provinces/ProvinceCollection.cs +++ b/ImperatorToCK3/CK3/Provinces/ProvinceCollection.cs @@ -10,6 +10,7 @@ using ImperatorToCK3.Mappers.Province; using ImperatorToCK3.Mappers.Religion; using Microsoft.VisualBasic.FileIO; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -84,8 +85,8 @@ public void ImportVanillaProvinces(ModFilesystem ck3ModFs, ReligionCollection re Logger.IncrementProgress(); // Cleanup: remove invalid faith and culture entries from province history - var validFaithIds = religions.Faiths.Select(f => f.Id).ToHashSet(); - var validCultureIds = cultures.Select(c => c.Id).ToHashSet(); + var validFaithIds = religions.Faiths.Select(f => f.Id).ToFrozenSet(); + var validCultureIds = cultures.Select(c => c.Id).ToFrozenSet(); foreach (var province in this) { var faithField = province.History.Fields["faith"]; int removedCount = faithField.RemoveAllEntries(value => !validFaithIds.Contains(value.ToString()?.RemQuotes() ?? string.Empty)); diff --git a/ImperatorToCK3/CK3/Provinces/ProvinceHistory.cs b/ImperatorToCK3/CK3/Provinces/ProvinceHistory.cs index 69bf23ef4..aa41ecad3 100644 --- a/ImperatorToCK3/CK3/Provinces/ProvinceHistory.cs +++ b/ImperatorToCK3/CK3/Provinces/ProvinceHistory.cs @@ -4,7 +4,7 @@ using ImperatorToCK3.CommonUtils; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.CK3.Provinces; @@ -68,11 +68,11 @@ public IReadOnlyCollection GetBuildings(Date date) { var buildingsValue = History.GetFieldValue("buildings", date); switch (buildingsValue) { case IList buildingsList: - return buildingsList.Select(b => b.ToString()!).ToImmutableList(); + return buildingsList.AsValueEnumerable().Select(b => b.ToString()!).ToImmutableList(); case IList buildingsList: return buildingsList.ToImmutableList(); case IList buildingsList: - return buildingsList.Select(b=>b.ToString()).ToImmutableList(); + return buildingsList.AsValueEnumerable().Select(b=>b.ToString()).ToImmutableList(); default: Logger.Warn($"Wrong province buildings value: {buildingsValue}"); return ImmutableList.Empty; diff --git a/ImperatorToCK3/CK3/Titles/LandedTitles.cs b/ImperatorToCK3/CK3/Titles/LandedTitles.cs index 058b6d10f..afd924f0a 100644 --- a/ImperatorToCK3/CK3/Titles/LandedTitles.cs +++ b/ImperatorToCK3/CK3/Titles/LandedTitles.cs @@ -23,6 +23,7 @@ using ImperatorToCK3.Mappers.TagTitle; using Open.Collections; using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; @@ -78,7 +79,7 @@ public void LoadTitles(ModFilesystem ck3ModFS, CK3LocDB ck3LocDB) { } // Cleanup for titles having invalid capital counties. - var validTitleIds = this.Select(t => t.Id).ToHashSet(); + var validTitleIds = this.Select(t => t.Id).ToFrozenSet(); var placeholderCountyId = validTitleIds.Order().First(t => t.StartsWith("c_")); foreach (var title in this.Where(t => t.Rank > TitleRank.county)) { if (title.CapitalCountyId is null && !title.Landless) { @@ -637,7 +638,7 @@ List countyLevelGovernorships var governorships = irWorld.JobsDB.Governorships; var governorshipsPerRegion = governorships.GroupBy(g => g.Region.Id) - .ToDictionary(g => g.Key, g => g.Count()); + .ToFrozenDictionary(g => g.Key, g => g.Count()); // landedTitles holds all titles imported from CK3. We'll now overwrite some and // add new ones from Imperator governorships. @@ -937,8 +938,8 @@ public void RemoveInvalidLandlessTitles(Date ck3BookmarkDate) { private void SetDeJureKingdoms(CK3LocDB ck3LocDB, Date ck3BookmarkDate) { Logger.Info("Setting de jure kingdoms..."); - var duchies = this.Where(t => t.Rank == TitleRank.duchy).ToHashSet(); - var duchiesWithDeJureVassals = duchies.Where(d => d.DeJureVassals.Count > 0).ToHashSet(); + var duchies = this.Where(t => t.Rank == TitleRank.duchy).ToFrozenSet(); + var duchiesWithDeJureVassals = duchies.Where(d => d.DeJureVassals.Count > 0).ToFrozenSet(); foreach (var duchy in duchiesWithDeJureVassals) { // If capital county belongs to an empire and contains the empire's capital, @@ -1052,12 +1053,12 @@ private void SetDeJureEmpires(CultureCollection ck3Cultures, CharacterCollection Logger.Debug("Building kingdom adjacencies dict..."); // Create a cache of province IDs per kingdom. var provincesPerKingdomDict = deJureKingdoms - .ToDictionary( + .ToFrozenDictionary( k => k.Id, - k => k.GetDeJureVassalsAndBelow("c").Values.SelectMany(c => c.CountyProvinceIds).ToHashSet() + k => k.GetDeJureVassalsAndBelow("c").Values.SelectMany(c => c.CountyProvinceIds).ToFrozenSet() ); - var kingdomAdjacenciesByLand = deJureKingdoms.ToDictionary(k => k.Id, _ => new ConcurrentHashSet()); - var kingdomAdjacenciesByWaterBody = deJureKingdoms.ToDictionary(k => k.Id, _ => new ConcurrentHashSet()); + var kingdomAdjacenciesByLand = deJureKingdoms.ToFrozenDictionary(k => k.Id, _ => new ConcurrentHashSet()); + var kingdomAdjacenciesByWaterBody = deJureKingdoms.ToFrozenDictionary(k => k.Id, _ => new ConcurrentHashSet()); Parallel.ForEach(deJureKingdoms, kingdom => { FindKingdomsAdjacentToKingdom(ck3MapData, deJureKingdoms, kingdom.Id, provincesPerKingdomDict, kingdomAdjacenciesByLand, kingdomAdjacenciesByWaterBody); }); @@ -1122,10 +1123,10 @@ Date ck3BookmarkDate private static void FindKingdomsAdjacentToKingdom( MapData ck3MapData, - IReadOnlyCollection deJureKingdoms, - string kingdomId, Dictionary<string, HashSet<ulong>> provincesPerKingdomDict, - Dictionary<string, ConcurrentHashSet<string>> kingdomAdjacenciesByLand, - Dictionary<string, ConcurrentHashSet<string>> kingdomAdjacenciesByWaterBody) + ImmutableArray<Title> deJureKingdoms, + string kingdomId, FrozenDictionary<string, FrozenSet<ulong>> provincesPerKingdomDict, + FrozenDictionary<string, ConcurrentHashSet<string>> kingdomAdjacenciesByLand, + FrozenDictionary<string, ConcurrentHashSet<string>> kingdomAdjacenciesByWaterBody) { foreach (var otherKingdom in deJureKingdoms) { // Since this code is parallelized, make sure we don't check the same pair twice. @@ -1185,8 +1186,8 @@ private Title CreateEmpireForHeritage(Pillar heritage, CultureCollection ck3Cult } private void SplitDisconnectedEmpires( - Dictionary<string, ConcurrentHashSet<string>> kingdomAdjacenciesByLand, - Dictionary<string, ConcurrentHashSet<string>> kingdomAdjacenciesByWaterBody, + FrozenDictionary<string, ConcurrentHashSet<string>> kingdomAdjacenciesByLand, + FrozenDictionary<string, ConcurrentHashSet<string>> kingdomAdjacenciesByWaterBody, HashSet<string> removableEmpireIds, Dictionary<string, ImmutableArray<Pillar>> kingdomToDominantHeritagesDict, Dictionary<string, Title> heritageToEmpireDict, @@ -1229,7 +1230,7 @@ Date date var adjacentEmpiresByLand = kingdomAdjacenciesByLand[kingdom.Id].Select(k => this[k].DeJureLiege) .Where(e => e is not null) .Select(e => e!) - .ToHashSet(); + .ToFrozenSet(); // Try to find valid neighbor by land first, to reduce the number of exclaves. Title? validNeighbor = null; @@ -1251,7 +1252,7 @@ Date date var adjacentEmpiresByWaterBody = kingdomAdjacenciesByWaterBody[kingdom.Id].Select(k => this[k].DeJureLiege) .Where(e => e is not null) .Select(e => e!) - .ToHashSet(); + .ToFrozenSet(); foreach (var secondaryHeritage in dominantHeritages.Skip(1)) { if (!heritageToEmpireDict.TryGetValue(secondaryHeritage.Id, out var heritageEmpire)) { @@ -1334,7 +1335,7 @@ IReadOnlySet<string> removableEmpireIds // Unassign de jure kingdoms that have no de jure land themselves. var deJureKingdomsWithoutLand = - deJureKingdoms.Where(k => k.GetDeJureVassalsAndBelow("c").Count == 0).ToHashSet(); + deJureKingdoms.Where(k => k.GetDeJureVassalsAndBelow("c").Count == 0).ToFrozenSet(); foreach (var deJureKingdomWithLand in deJureKingdomsWithoutLand) { deJureKingdomWithLand.DeJureLiege = null; } @@ -1392,13 +1393,13 @@ IReadOnlySet<string> removableEmpireIds return dictToReturn; } - private static bool AreTitlesAdjacent(HashSet<ulong> title1ProvinceIds, HashSet<ulong> title2ProvinceIds, MapData mapData) { + private static bool AreTitlesAdjacent(FrozenSet<ulong> title1ProvinceIds, FrozenSet<ulong> title2ProvinceIds, MapData mapData) { return mapData.AreProvinceGroupsAdjacent(title1ProvinceIds, title2ProvinceIds); } - private static bool AreTitlesAdjacentByLand(HashSet<ulong> title1ProvinceIds, HashSet<ulong> title2ProvinceIds, MapData mapData) { + private static bool AreTitlesAdjacentByLand(FrozenSet<ulong> title1ProvinceIds, FrozenSet<ulong> title2ProvinceIds, MapData mapData) { return mapData.AreProvinceGroupsAdjacentByLand(title1ProvinceIds, title2ProvinceIds); } - private static bool AreTitlesAdjacentByWaterBody(HashSet<ulong> title1ProvinceIds, HashSet<ulong> title2ProvinceIds, MapData mapData) { + private static bool AreTitlesAdjacentByWaterBody(FrozenSet<ulong> title1ProvinceIds, FrozenSet<ulong> title2ProvinceIds, MapData mapData) { return mapData.AreProvinceGroupsConnectedByWaterBody(title1ProvinceIds, title2ProvinceIds); } @@ -1612,9 +1613,14 @@ public void ImportImperatorGovernmentOffices(ICollection<OfficeJob> irOfficeJobs string[] ignoredOfficeTypes = ["office_plebeian_aedile"]; // Log all unhandled office types. - var irOfficeTypesFromSave = irOfficeJobs.Select(j => j.OfficeType).ToHashSet(); - var handledOfficeTypes = councilPositionToSourcesDict.Values.SelectMany(v => v).Concat(courtPositionToSourcesDict.Values.SelectMany(v => v)).Concat(ignoredOfficeTypes).ToHashSet(); - var unmappedOfficeTypes = irOfficeTypesFromSave.Where(officeType => !handledOfficeTypes.Contains(officeType)).ToArray(); + var irOfficeTypesFromSave = irOfficeJobs.Select(j => j.OfficeType).ToFrozenSet(); + var handledOfficeTypes = councilPositionToSourcesDict.Values + .SelectMany(v => v) + .Concat(courtPositionToSourcesDict.Values.SelectMany(v => v)) + .Concat(ignoredOfficeTypes) + .ToFrozenSet(); + var unmappedOfficeTypes = irOfficeTypesFromSave + .Where(officeType => !handledOfficeTypes.Contains(officeType)).ToArray(); if (unmappedOfficeTypes.Length > 0) { Logger.Error($"Unmapped office types: {string.Join(", ", unmappedOfficeTypes)}"); } @@ -1650,16 +1656,16 @@ public IReadOnlyCollection<Title> GetDeJureDuchies() => this .Where(t => t is {Rank: TitleRank.duchy, DeJureVassals.Count: > 0}) .ToImmutableArray(); - public IReadOnlyCollection<Title> GetDeJureKingdoms() => this + public ImmutableArray<Title> GetDeJureKingdoms() => this .Where(t => t is {Rank: TitleRank.kingdom, DeJureVassals.Count: > 0}) .ToImmutableArray(); - private HashSet<Color> UsedColors => this.Select(t => t.Color1).Where(c => c is not null).ToHashSet()!; + private FrozenSet<Color> UsedColors => this.Select(t => t.Color1).Where(c => c is not null).ToFrozenSet()!; public bool IsColorUsed(Color color) { return UsedColors.Contains(color); } public Color GetDerivedColor(Color baseColor) { - HashSet<Color> usedHueColors = UsedColors.Where(c => Math.Abs(c.H - baseColor.H) < 0.001).ToHashSet(); + FrozenSet<Color> usedHueColors = UsedColors.Where(c => Math.Abs(c.H - baseColor.H) < 0.001).ToFrozenSet(); for (double v = 0.05; v <= 1; v += 0.02) { var newColor = new Color(baseColor.H, baseColor.S, v); diff --git a/ImperatorToCK3/CK3/Wars/War.cs b/ImperatorToCK3/CK3/Wars/War.cs index 06dea11f4..4d20abaed 100644 --- a/ImperatorToCK3/CK3/Wars/War.cs +++ b/ImperatorToCK3/CK3/Wars/War.cs @@ -5,6 +5,7 @@ using ImperatorToCK3.Exceptions; using ImperatorToCK3.Imperator.States; using ImperatorToCK3.Mappers.Province; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; @@ -53,7 +54,7 @@ public War(Imperator.Diplomacy.War irWar, Mappers.War.WarMapper warMapper, Provi .Where(t => t is not null) .Cast<Title>() .Select(t => t.Id) - .ToHashSet(); + .ToFrozenSet(); TargetedTitles.UnionWith(targetedCountyIds); } diff --git a/ImperatorToCK3/CK3/World.cs b/ImperatorToCK3/CK3/World.cs index fe49fcc32..e3661de29 100644 --- a/ImperatorToCK3/CK3/World.cs +++ b/ImperatorToCK3/CK3/World.cs @@ -38,6 +38,7 @@ using System.Threading; using System.Threading.Tasks; using Open.Collections; +using System.Collections.Frozen; namespace ImperatorToCK3.CK3; @@ -503,10 +504,10 @@ private void ClearFeaturedCharactersDescriptions(Date ck3BookmarkDate) { } } - private void OverwriteCountiesHistory(CountryCollection irCountries, IEnumerable<Governorship> governorships, IList<KeyValuePair<Country, Dependency?>> countyLevelCountries, IEnumerable<Governorship> countyLevelGovernorships, Imperator.Characters.CharacterCollection impCharacters, Imperator.Provinces.ProvinceCollection irProvinces, Date conversionDate) { + private void OverwriteCountiesHistory(CountryCollection irCountries, List<Governorship> governorships, List<KeyValuePair<Country, Dependency?>> countyLevelCountries, List<Governorship> countyLevelGovernorships, Imperator.Characters.CharacterCollection impCharacters, Imperator.Provinces.ProvinceCollection irProvinces, Date conversionDate) { Logger.Info("Overwriting counties' history..."); - HashSet<Governorship> governorshipsSet = governorships.ToHashSet(); - HashSet<Governorship> countyLevelGovernorshipsSet = countyLevelGovernorships.ToHashSet(); + FrozenSet<Governorship> governorshipsSet = governorships.ToFrozenSet(); + FrozenSet<Governorship> countyLevelGovernorshipsSet = countyLevelGovernorships.ToFrozenSet(); foreach (var county in LandedTitles.Where(t => t.Rank == TitleRank.county)) { if (county.CapitalBaronyProvinceId is null) { @@ -568,9 +569,9 @@ private bool TryGiveCountyToMonarch(Title county, Country irCountry) { private bool TryGiveCountyToGovernor(Title county, Imperator.Provinces.Province irProvince, Country irCountry, - HashSet<Governorship> governorshipsSet, + FrozenSet<Governorship> governorshipsSet, Imperator.Provinces.ProvinceCollection irProvinces, - HashSet<Governorship> countyLevelGovernorshipsSet, + FrozenSet<Governorship> countyLevelGovernorshipsSet, Imperator.Characters.CharacterCollection irCharacters) { var ck3Country = irCountry.CK3Title; if (ck3Country is null) { @@ -658,7 +659,7 @@ private static void GiveCountyToCountyLevelGovernor(Title county, private bool TryGiveCountyToCountyLevelRuler(Title county, Country irCountry, - IList<KeyValuePair<Country, Dependency?>> countyLevelCountries, + List<KeyValuePair<Country, Dependency?>> countyLevelCountries, CountryCollection irCountries) { var matchingCountyLevelRulers = countyLevelCountries.Where(c => c.Key.Id == irCountry.Id).ToArray(); if (matchingCountyLevelRulers.Length == 0) { @@ -911,12 +912,12 @@ private void UseClosestProvincesToRemoveIslam(HashSet<Province> muslimProvinces, var provincesWithValidFaith = Provinces .Except(muslimProvinces) .Where(p => p.GetFaithId(date) is not null) - .ToHashSet(); + .ToFrozenSet(); foreach (var province in muslimProvinces) { var closestValidProvince = provincesWithValidFaith .Except(muslimProvinces) .Select(p => new { - Province = p, + Province = p, Distance = MapData.GetDistanceBetweenProvinces(province.Id, p.Id), }) .Where(x => x.Distance > 0) @@ -1088,7 +1089,7 @@ private void GenerateFillerHoldersForUnownedLands(CultureCollection cultures, Co .Select(p => p!.GetHoldingType(date)) .Where(t => t is not null) .Select(t => t!) - .ToHashSet(); + .ToFrozenSet(); string government = countyHoldingTypes.Contains("castle_holding") ? "feudal_government" : "tribal_government"; diff --git a/ImperatorToCK3/CommonUtils/DiffHistoryField.cs b/ImperatorToCK3/CommonUtils/DiffHistoryField.cs index acbf5c196..5c16cf2ba 100644 --- a/ImperatorToCK3/CommonUtils/DiffHistoryField.cs +++ b/ImperatorToCK3/CommonUtils/DiffHistoryField.cs @@ -6,7 +6,7 @@ namespace ImperatorToCK3.CommonUtils; internal sealed class DiffHistoryField : IHistoryField { public string Id { get; } - public IList<KeyValuePair<string, object>> InitialEntries { get; } = new List<KeyValuePair<string, object>>(); + public List<KeyValuePair<string, object>> InitialEntries { get; } = []; public SortedDictionary<Date, List<KeyValuePair<string, object>>> DateToEntriesDict { get; } = new(); diff --git a/ImperatorToCK3/CommonUtils/History.cs b/ImperatorToCK3/CommonUtils/History.cs index 91f8b8fba..b760c584b 100644 --- a/ImperatorToCK3/CommonUtils/History.cs +++ b/ImperatorToCK3/CommonUtils/History.cs @@ -7,7 +7,7 @@ namespace ImperatorToCK3.CommonUtils; -public sealed class History : IPDXSerializable { +internal sealed class History : IPDXSerializable { [NonSerialized] public IdObjectCollection<string, IHistoryField> Fields { get; } = []; // fieldName, field [NonSerialized] public IgnoredKeywordsSet IgnoredKeywords { get; } = []; diff --git a/ImperatorToCK3/CommonUtils/HistoryFactory.cs b/ImperatorToCK3/CommonUtils/HistoryFactory.cs index deb67fd46..822e1f8ec 100644 --- a/ImperatorToCK3/CommonUtils/HistoryFactory.cs +++ b/ImperatorToCK3/CommonUtils/HistoryFactory.cs @@ -5,7 +5,7 @@ using System.IO; namespace ImperatorToCK3.CommonUtils; -public sealed class HistoryFactory { +internal sealed class HistoryFactory { public sealed class HistoryFactoryBuilder { private readonly List<SimpleFieldDef> simpleFieldDefs = []; // fieldName, setters, initialValue private readonly List<SimpleFieldDef> literalFieldDefs = []; // fieldName, setters, initialValue diff --git a/ImperatorToCK3/CommonUtils/IHistoryField.cs b/ImperatorToCK3/CommonUtils/IHistoryField.cs index 7b651bad9..66c7eb26c 100644 --- a/ImperatorToCK3/CommonUtils/IHistoryField.cs +++ b/ImperatorToCK3/CommonUtils/IHistoryField.cs @@ -2,18 +2,18 @@ using commonItems.Collections; using System; using System.Collections.Generic; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.CommonUtils; -public interface IHistoryField : IIdentifiable<string> { - public IList<KeyValuePair<string, object>> InitialEntries { get; } +internal interface IHistoryField : IIdentifiable<string> { + public List<KeyValuePair<string, object>> InitialEntries { get; } public SortedDictionary<Date, List<KeyValuePair<string, object>>> DateToEntriesDict { get; } public object? GetValue(Date date); public void RemoveHistoryPastDate(Date date) { - foreach (var item in DateToEntriesDict.Where(kv => kv.Key > date).ToArray()) { + foreach (var item in DateToEntriesDict.AsValueEnumerable().Where(kv => kv.Key > date).ToArray()) { DateToEntriesDict.Remove(item.Key); } } diff --git a/ImperatorToCK3/CommonUtils/LiteralHistoryField.cs b/ImperatorToCK3/CommonUtils/LiteralHistoryField.cs index 71887ed66..e30974223 100644 --- a/ImperatorToCK3/CommonUtils/LiteralHistoryField.cs +++ b/ImperatorToCK3/CommonUtils/LiteralHistoryField.cs @@ -6,9 +6,9 @@ namespace ImperatorToCK3.CommonUtils; -public sealed class LiteralHistoryField : IHistoryField { +internal sealed class LiteralHistoryField : IHistoryField { public string Id { get; } - public IList<KeyValuePair<string, object>> InitialEntries { get; } = new List<KeyValuePair<string, object>>(); // every entry is a <setter, value> pair + public List<KeyValuePair<string, object>> InitialEntries { get; } = []; // every entry is a <setter, value> pair public SortedDictionary<Date, List<KeyValuePair<string, object>>> DateToEntriesDict { get; } = new(); diff --git a/ImperatorToCK3/CommonUtils/Map/MapData.cs b/ImperatorToCK3/CommonUtils/Map/MapData.cs index 60b8aced9..032b3c701 100644 --- a/ImperatorToCK3/CommonUtils/Map/MapData.cs +++ b/ImperatorToCK3/CommonUtils/Map/MapData.cs @@ -4,6 +4,7 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.IO; using System.Linq; @@ -119,7 +120,7 @@ private void GroupStaticWaterProvinces() { var staticWaterProvinces = ProvinceDefinitions .Where(p => p.IsStaticWater) .Select(p => p.Id) - .ToHashSet(); + .ToFrozenSet(); var provinceGroups = new List<HashSet<ulong>>(); foreach (var provinceId in staticWaterProvinces) { @@ -199,9 +200,9 @@ public IReadOnlySet<ulong> GetNeighborProvinceIds(ulong provinceId) { private bool IsStaticWater(ulong provinceId) => ProvinceDefinitions[provinceId].IsStaticWater; private bool IsRiver(ulong provinceId) => ProvinceDefinitions[provinceId].IsRiver; - public IReadOnlySet<ulong> ColorableImpassableProvinceIds => ProvinceDefinitions + public FrozenSet<ulong> ColorableImpassableProvinceIds => ProvinceDefinitions .Where(p => p.IsColorableImpassable).Select(p => p.Id) - .ToHashSet(); + .ToFrozenSet(); public IReadOnlySet<ulong> MapEdgeProvinceIds => mapEdgeProvinces; @@ -349,11 +350,11 @@ private void AddNeighbor(ulong mainProvince, ulong neighborProvince) { } /// Function for checking if two provinces are directly neighboring or border the same static water body. - public bool AreProvinceGroupsAdjacent(HashSet<ulong> group1, HashSet<ulong> group2) { + public bool AreProvinceGroupsAdjacent(FrozenSet<ulong> group1, FrozenSet<ulong> group2) { return AreProvinceGroupsAdjacentByLand(group1, group2) || AreProvinceGroupsConnectedByWaterBody(group1, group2); } - public bool AreProvinceGroupsAdjacentByLand(HashSet<ulong> group1, HashSet<ulong> group2) { + public bool AreProvinceGroupsAdjacentByLand(FrozenSet<ulong> group1, FrozenSet<ulong> group2) { var group1Neighbors = new HashSet<ulong>(); foreach (var province in group1) { if (NeighborsDict.TryGetValue(province, out var neighbors)) { @@ -377,7 +378,7 @@ public bool AreProvinceGroupsAdjacentByLand(HashSet<ulong> group1, HashSet<ulong var group2RiverProvinceNeighbors = group2 .SelectMany(provId => NeighborsDict.TryGetValue(provId, out var neighbors) ? neighbors : []) .Where(IsRiver) - .ToHashSet(); + .ToFrozenSet(); if (group1Neighbors.Overlaps(group2RiverProvinceNeighbors)) { return true; } @@ -386,7 +387,7 @@ public bool AreProvinceGroupsAdjacentByLand(HashSet<ulong> group1, HashSet<ulong } // Function for checking if two land provinces are connected to the same water body. - public bool AreProvinceGroupsConnectedByWaterBody(HashSet<ulong> group1, HashSet<ulong> group2) { + public bool AreProvinceGroupsConnectedByWaterBody(FrozenSet<ulong> group1, FrozenSet<ulong> group2) { var group1WaterNeighbors = new HashSet<ulong>(); foreach (var provId in group1) { if (!NeighborsDict.TryGetValue(provId, out var neighbors)) { @@ -403,12 +404,12 @@ public bool AreProvinceGroupsConnectedByWaterBody(HashSet<ulong> group1, HashSet var group2WaterNeighbors = group2 .SelectMany(provId => NeighborsDict.TryGetValue(provId, out var neighbors) ? neighbors : []) .Where(IsStaticWater) - .ToHashSet(); + .ToFrozenSet(); if (group2WaterNeighbors.Count == 0) { return false; } - var group1WaterBodies = group1WaterNeighbors.Select(id => waterBodiesDict[id]).ToHashSet(); + var group1WaterBodies = group1WaterNeighbors.Select(id => waterBodiesDict[id]).ToFrozenSet(); return group2WaterNeighbors .Any(group2ProvId => group1WaterBodies.Contains(waterBodiesDict[group2ProvId])); diff --git a/ImperatorToCK3/CommonUtils/SimpleHistoryField.cs b/ImperatorToCK3/CommonUtils/SimpleHistoryField.cs index e760fc65b..8ae5a5777 100644 --- a/ImperatorToCK3/CommonUtils/SimpleHistoryField.cs +++ b/ImperatorToCK3/CommonUtils/SimpleHistoryField.cs @@ -5,9 +5,9 @@ namespace ImperatorToCK3.CommonUtils; -public sealed class SimpleHistoryField : IHistoryField { +internal sealed class SimpleHistoryField : IHistoryField { public string Id { get; } - public IList<KeyValuePair<string, object>> InitialEntries { get; } = []; // every entry is a <setter, value> pair + public List<KeyValuePair<string, object>> InitialEntries { get; } = []; // every entry is a <setter, value> pair public SortedDictionary<Date, List<KeyValuePair<string, object>>> DateToEntriesDict { get; } = []; diff --git a/ImperatorToCK3/Imperator/Armies/Unit.cs b/ImperatorToCK3/Imperator/Armies/Unit.cs index f1daa8e91..1a6b9012c 100644 --- a/ImperatorToCK3/Imperator/Armies/Unit.cs +++ b/ImperatorToCK3/Imperator/Armies/Unit.cs @@ -2,6 +2,7 @@ using commonItems.Collections; using commonItems.Localization; using ImperatorToCK3.CommonUtils; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; @@ -17,7 +18,7 @@ internal sealed class Unit : IIdentifiable<ulong> { private List<ulong> CohortIds { get; } = new(); public LocBlock? LocalizedName { get; private set; } - public Dictionary<string, int> MenPerUnitType { get; } + public FrozenDictionary<string, int> MenPerUnitType { get; } public Unit(ulong id, BufferedReader legionReader, UnitCollection unitCollection, LocDB irLocDB, ImperatorDefines defines) { Id = id; @@ -83,12 +84,12 @@ public Unit(ulong id, BufferedReader legionReader, UnitCollection unitCollection return nameLocBlock; } - private Dictionary<string, int> GetMenPerUnitType(UnitCollection unitCollection, ImperatorDefines defines) { + private FrozenDictionary<string, int> GetMenPerUnitType(UnitCollection unitCollection, ImperatorDefines defines) { var cohortSize = defines.CohortSize; return unitCollection.Subunits.Where(s => CohortIds.Contains(s.Id)) .GroupBy(s=>s.Type) - .ToDictionary(g => g.Key, g => (int)g.Sum(s => cohortSize * s.Strength)); + .ToFrozenDictionary(g => g.Key, g => (int)g.Sum(s => cohortSize * s.Strength)); } public static IgnoredKeywordsSet IgnoredTokens { get; } = []; diff --git a/ImperatorToCK3/Imperator/Characters/PortraitData.cs b/ImperatorToCK3/Imperator/Characters/PortraitData.cs index 82ef1d7d0..d5b7e1fc2 100644 --- a/ImperatorToCK3/Imperator/Characters/PortraitData.cs +++ b/ImperatorToCK3/Imperator/Characters/PortraitData.cs @@ -1,8 +1,9 @@ using commonItems; using ImperatorToCK3.CommonUtils.Genes; using System; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.Imperator.Characters; @@ -16,6 +17,8 @@ internal sealed class PortraitData { public Dictionary<string, AccessoryGeneData> AccessoryGenesDict { get; } = []; public Dictionary<string, MorphGeneData> MorphGenesDict { get; } = []; + + private static readonly FrozenSet<string> morphGenesToIgnore = ["expression"]; public PortraitData(string dnaString, GenesDB genesDB, string ageSexString = "male") { var decodedDnaStr = Convert.FromBase64String(dnaString); @@ -40,8 +43,7 @@ public PortraitData(string dnaString, GenesDB genesDB, string ageSexString = "ma EyeColor2PaletteCoordinates.Y = decodedDnaStr[eyeColorPaletteXIndex + 3] * 2; // morph genes - var morphGenesToIgnore = new[] {"expression"}; - var morphGenesToLoad = genesDB.MorphGenes + var morphGenesToLoad = genesDB.MorphGenes.AsValueEnumerable() .Where(g => !morphGenesToIgnore.Contains(g.Id)); foreach (var gene in morphGenesToLoad) { var geneIndex = gene.Index; diff --git a/ImperatorToCK3/Imperator/Countries/Country.cs b/ImperatorToCK3/Imperator/Countries/Country.cs index 622f45f45..9bb609541 100644 --- a/ImperatorToCK3/Imperator/Countries/Country.cs +++ b/ImperatorToCK3/Imperator/Countries/Country.cs @@ -4,6 +4,7 @@ using ImperatorToCK3.Imperator.Families; using ImperatorToCK3.Imperator.Inventions; using ImperatorToCK3.Imperator.Provinces; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; @@ -17,7 +18,7 @@ internal sealed partial class Country : IIdentifiable<ulong> { public string? PrimaryCulture { get; private set; } public string? Religion { get; private set; } public List<RulerTerm> RulerTerms { get; set; } = []; - public Dictionary<string, int> HistoricalRegnalNumbers { get; private set; } = []; + public FrozenDictionary<string, int> HistoricalRegnalNumbers { get; private set; } = FrozenDictionary<string, int>.Empty; private string tag = ""; public string Tag { diff --git a/ImperatorToCK3/Imperator/Countries/CountryCollection.cs b/ImperatorToCK3/Imperator/Countries/CountryCollection.cs index fd0ed9f45..f4400e130 100644 --- a/ImperatorToCK3/Imperator/Countries/CountryCollection.cs +++ b/ImperatorToCK3/Imperator/Countries/CountryCollection.cs @@ -2,9 +2,9 @@ using commonItems.Collections; using ImperatorToCK3.Imperator.Families; using System.Collections.Generic; -using System.Linq; using System.Threading.Channels; using System.Threading.Tasks; +using ZLinq; namespace ImperatorToCK3.Imperator.Countries; @@ -61,7 +61,7 @@ public void LoadCountries(BufferedReader reader) { public void LinkFamilies(FamilyCollection families) { SortedSet<ulong> idsWithoutDefinition = new(); - var counter = this.Sum(country => country.LinkFamilies(families, idsWithoutDefinition)); + var counter = this.AsValueEnumerable().Sum(country => country.LinkFamilies(families, idsWithoutDefinition)); if (idsWithoutDefinition.Count > 0) { Logger.Debug($"Families without definition: {string.Join(", ", idsWithoutDefinition)}"); diff --git a/ImperatorToCK3/Imperator/Countries/CountryFactory.cs b/ImperatorToCK3/Imperator/Countries/CountryFactory.cs index e55d5fc80..ed23f893d 100644 --- a/ImperatorToCK3/Imperator/Countries/CountryFactory.cs +++ b/ImperatorToCK3/Imperator/Countries/CountryFactory.cs @@ -2,6 +2,7 @@ using commonItems.Colors; using commonItems.Mods; using ImperatorToCK3.CommonUtils; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -46,7 +47,7 @@ private static void RegisterCountryKeywords(Parser parser, Country parsedCountry parser.RegisterKeyword("historical_regnal_numbers", reader => { parsedCountry.HistoricalRegnalNumbers = reader.GetAssignments() .GroupBy(a => a.Key) - .ToDictionary(g => g.Key, g => int.Parse(g.Last().Value)); + .ToFrozenDictionary(g => g.Key, g => int.Parse(g.Last().Value)); }); parser.RegisterKeyword("primary_culture", reader => parsedCountry.PrimaryCulture = reader.GetString()); parser.RegisterKeyword("religion", reader => parsedCountry.Religion = reader.GetString()); diff --git a/ImperatorToCK3/Imperator/Jobs/Governorship.cs b/ImperatorToCK3/Imperator/Jobs/Governorship.cs index f8b2fb04a..12e31b953 100644 --- a/ImperatorToCK3/Imperator/Jobs/Governorship.cs +++ b/ImperatorToCK3/Imperator/Jobs/Governorship.cs @@ -45,13 +45,13 @@ public Governorship(BufferedReader governorshipReader, CountryCollection countri } } - public IReadOnlyCollection<Province> GetIRProvinces(ProvinceCollection irProvinces) { + public ImmutableArray<Province> GetIRProvinces(ProvinceCollection irProvinces) { return irProvinces .Where(p => p.OwnerCountry == Country && Region.ContainsProvince(p.Id)) .ToImmutableArray(); } - public IReadOnlyCollection<ulong> GetCK3ProvinceIds(ProvinceCollection irProvinces, ProvinceMapper provMapper) { + public ImmutableArray<ulong> GetCK3ProvinceIds(ProvinceCollection irProvinces, ProvinceMapper provMapper) { return GetIRProvinces(irProvinces) .Select(p => p.Id) .SelectMany(provMapper.GetCK3ProvinceNumbers) diff --git a/ImperatorToCK3/ImperatorToCK3.csproj b/ImperatorToCK3/ImperatorToCK3.csproj index 9985f3744..529921327 100644 --- a/ImperatorToCK3/ImperatorToCK3.csproj +++ b/ImperatorToCK3/ImperatorToCK3.csproj @@ -51,6 +51,7 @@ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="SixLabors.ImageSharp" Version="3.1.8"/> + <PackageReference Include="ZLinq" Version="1.4.7" /> </ItemGroup> <ItemGroup> diff --git a/ImperatorToCK3/Mappers/CoA/CoaMapper.cs b/ImperatorToCK3/Mappers/CoA/CoaMapper.cs index 1112f4db1..a6962c064 100644 --- a/ImperatorToCK3/Mappers/CoA/CoaMapper.cs +++ b/ImperatorToCK3/Mappers/CoA/CoaMapper.cs @@ -1,5 +1,6 @@ using commonItems; using commonItems.Mods; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; @@ -49,9 +50,9 @@ public void ParseCoAs(IEnumerable<string> coaDefinitionStrings) { /// <summary> /// For a given collection of flag names, returns ones that don't have a defined CoA. /// </summary> - public HashSet<string> GetAllMissingFlagKeys(IEnumerable<string> flagKeys) { - var existingFlagKeys = coasMap.Keys.ToHashSet(); - return flagKeys.Where(flagKey => !existingFlagKeys.Contains(flagKey)).ToHashSet(); + public FrozenSet<string> GetAllMissingFlagKeys(IEnumerable<string> flagKeys) { + var existingFlagKeys = coasMap.Keys.ToFrozenSet(); + return flagKeys.Where(flagKey => !existingFlagKeys.Contains(flagKey)).ToFrozenSet(); } private readonly Dictionary<string, string> coasMap = []; diff --git a/ImperatorToCK3/Mappers/Region/CK3Region.cs b/ImperatorToCK3/Mappers/Region/CK3Region.cs index 198a3be5e..98b660845 100644 --- a/ImperatorToCK3/Mappers/Region/CK3Region.cs +++ b/ImperatorToCK3/Mappers/Region/CK3Region.cs @@ -1,7 +1,7 @@ using commonItems; using ImperatorToCK3.CK3.Titles; using System.Collections.Generic; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.Mappers.Region; @@ -55,13 +55,13 @@ public void LinkCounty(Title theCounty) { Counties[theCounty.Id] = theCounty; } public bool ContainsProvince(ulong provinceId) { - if (Regions.Values.Any(region => region.ContainsProvince(provinceId))) { + if (Regions.Values.AsValueEnumerable().Any(region => region.ContainsProvince(provinceId))) { return true; } - if (Duchies.Values.Any(duchy => duchy.DuchyContainsProvince(provinceId))) { + if (Duchies.Values.AsValueEnumerable().Any(duchy => duchy.DuchyContainsProvince(provinceId))) { return true; } - if (Counties.Values.Any(county => county.CountyProvinceIds.Contains(provinceId))) { + if (Counties.Values.AsValueEnumerable().Any(county => county.CountyProvinceIds.AsValueEnumerable().Contains(provinceId))) { return true; } return Provinces.Contains(provinceId); diff --git a/ImperatorToCK3/Mappers/Region/CK3RegionMapper.cs b/ImperatorToCK3/Mappers/Region/CK3RegionMapper.cs index f1771f7ba..40e00cd9a 100644 --- a/ImperatorToCK3/Mappers/Region/CK3RegionMapper.cs +++ b/ImperatorToCK3/Mappers/Region/CK3RegionMapper.cs @@ -3,7 +3,7 @@ using ImperatorToCK3.CK3.Titles; using System.Collections.Generic; using System.IO; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.Mappers.Region; @@ -39,7 +39,7 @@ public void LoadRegions(ModFilesystem ck3ModFS, Title.LandedTitles landedTitles) // Log duchies that don't have any de jure counties. // Such duchies should probably be removed from the regions. - var validDeJureDuchyIds = landedTitles.GetDeJureDuchies().Select(d => d.Id).ToHashSet(); + var validDeJureDuchyIds = landedTitles.GetDeJureDuchies().AsValueEnumerable().Select(d => d.Id).ToFrozenSet(); foreach (var region in regions.Values) { foreach (var regionDuchyId in region.Duchies.Keys) { if (!validDeJureDuchyIds.Contains(regionDuchyId)) { @@ -59,7 +59,7 @@ public bool ProvinceIsInRegion(ulong provinceId, string regionName) { } // And sometimes they don't mean what people think they mean at all. - return counties.TryGetValue(regionName, out var county) && county.CountyProvinceIds.Contains(provinceId); + return counties.TryGetValue(regionName, out var county) && county.CountyProvinceIds.AsValueEnumerable().Contains(provinceId); } public bool RegionNameIsValid(string regionName) { if (regions.ContainsKey(regionName)) { @@ -79,7 +79,7 @@ public bool RegionNameIsValid(string regionName) { } public string? GetParentCountyName(ulong provinceId) { foreach (var (countyName, county) in counties) { - if (county.CountyProvinceIds.Contains(provinceId)) { + if (county.CountyProvinceIds.AsValueEnumerable().Contains(provinceId)) { return countyName; } } diff --git a/ImperatorToCK3/Mappers/Region/ImperatorRegion.cs b/ImperatorToCK3/Mappers/Region/ImperatorRegion.cs index f552e6b1a..22b1039ba 100644 --- a/ImperatorToCK3/Mappers/Region/ImperatorRegion.cs +++ b/ImperatorToCK3/Mappers/Region/ImperatorRegion.cs @@ -4,7 +4,7 @@ using ImperatorToCK3.Imperator.Geography; using System; using System.Collections.Generic; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.Mappers.Region; @@ -49,7 +49,7 @@ private void RegisterKeys(Parser parser, ColorFactory colorFactory) { } public bool ContainsProvince(ulong province) { - return Areas.Any(area => area.ContainsProvince(province)); + return Areas.AsValueEnumerable().Any(area => area.ContainsProvince(province)); } private readonly HashSet<string> parsedAreas = new(); diff --git a/ImperatorToCK3/Mappers/Technology/InnovationMapper.cs b/ImperatorToCK3/Mappers/Technology/InnovationMapper.cs index 64982f650..fe95b1bfc 100644 --- a/ImperatorToCK3/Mappers/Technology/InnovationMapper.cs +++ b/ImperatorToCK3/Mappers/Technology/InnovationMapper.cs @@ -3,7 +3,7 @@ using ImperatorToCK3.CK3; using ImperatorToCK3.Imperator.Inventions; using System.Collections.Generic; -using System.Linq; +using ZLinq; namespace ImperatorToCK3.Mappers.Technology; @@ -56,11 +56,11 @@ public Dictionary<string, ushort> GetInnovationProgresses(ICollection<string> ir public void LogUnmappedInventions(InventionsDB inventionsDB, LocDB irLocDB) { // Log Imperator inventions for which neither link nor bonus for CK3 innovations exists. - var unmappedInventions = inventionsDB.InventionIds + var unmappedInventions = inventionsDB.InventionIds.AsValueEnumerable() .Where(invention => !innovationLinks.Exists(link => link.Match(invention) is not null) && !innovationBonuses.Exists(bonus => bonus.GetProgress([invention]) is not null)) .ToArray(); - var inventionsWithLoc = unmappedInventions + var inventionsWithLoc = unmappedInventions.AsValueEnumerable() .Select(inventionId => { if (irLocDB.GetLocBlockForKey(inventionId) is { } locBlock) { return $"{inventionId} ({locBlock[ConverterGlobals.PrimaryLanguage]})"; @@ -68,7 +68,7 @@ public void LogUnmappedInventions(InventionsDB inventionsDB, LocDB irLocDB) { return inventionId; }); - Logger.Debug($"Unmapped I:R inventions: {string.Join(", ", inventionsWithLoc)}"); + Logger.Debug($"Unmapped I:R inventions: {inventionsWithLoc.JoinToString(", ")}"); } // TODO: ALSO LOG UNMAPPED CK3 MARTIAL AND CIVIC INNOVATIONS diff --git a/ImperatorToCK3/Mappers/Trait/TraitMapper.cs b/ImperatorToCK3/Mappers/Trait/TraitMapper.cs index a6fdeb2da..5d91d581f 100644 --- a/ImperatorToCK3/Mappers/Trait/TraitMapper.cs +++ b/ImperatorToCK3/Mappers/Trait/TraitMapper.cs @@ -7,7 +7,7 @@ namespace ImperatorToCK3.Mappers.Trait; internal class TraitMapper { - protected IDictionary<string, string> ImperatorToCK3TraitMap = new Dictionary<string, string>(); + protected Dictionary<string, string> ImperatorToCK3TraitMap = []; protected IdObjectCollection<string, CK3.Characters.Trait> CK3Traits = []; public IEnumerable<string> ValidCK3TraitIDs => CK3Traits.Select(t => t.Id); diff --git a/ImperatorToCK3/Outputter/BookmarkOutputter.cs b/ImperatorToCK3/Outputter/BookmarkOutputter.cs index 63da5137e..c28f089f7 100644 --- a/ImperatorToCK3/Outputter/BookmarkOutputter.cs +++ b/ImperatorToCK3/Outputter/BookmarkOutputter.cs @@ -9,6 +9,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.IO; using System.Linq; @@ -267,8 +268,8 @@ Image bookmarkMapImage bookmarkMapImage.Mutate(x => x.DrawImage(realmHighlightImage, 0.5f)); } - private static HashSet<ulong> GetColorableImpassablesExceptMapEdgeProvinces(MapData mapData) { - return mapData.ColorableImpassableProvinceIds.Except(mapData.MapEdgeProvinceIds).ToHashSet(); + private static FrozenSet<ulong> GetColorableImpassablesExceptMapEdgeProvinces(MapData mapData) { + return mapData.ColorableImpassableProvinceIds.Except(mapData.MapEdgeProvinceIds).ToFrozenSet(); } private static HashSet<ulong> GetImpassableProvincesToColor(MapData mapData, ISet<ulong> heldProvinceIds) { @@ -277,7 +278,7 @@ private static HashSet<ulong> GetImpassableProvincesToColor(MapData mapData, ISe foreach (ulong impassableId in impassableIds) { var nonImpassableNeighborProvIds = mapData.GetNeighborProvinceIds(impassableId) .Except(impassableIds) - .ToHashSet(); + .ToFrozenSet(); if (nonImpassableNeighborProvIds.Count == 0) { continue; } diff --git a/ImperatorToCK3/Outputter/CharactersOutputter.cs b/ImperatorToCK3/Outputter/CharactersOutputter.cs index 351214268..ce1c5790f 100644 --- a/ImperatorToCK3/Outputter/CharactersOutputter.cs +++ b/ImperatorToCK3/Outputter/CharactersOutputter.cs @@ -3,12 +3,13 @@ using ImperatorToCK3.CK3.Characters; using ImperatorToCK3.CommonUtils; using Open.Collections; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using ZLinq; namespace ImperatorToCK3.Outputter; @@ -27,14 +28,14 @@ public static async Task OutputCharacters(string outputPath, CharacterCollection // Portrait modifiers need to be outputted before characters themselves, // because while outputting the portrait modifiers we're adding character flags to character history. - var charactersWithDNA = characters + var charactersWithDNA = characters.AsValueEnumerable() .Where(c => c.DNA is not null) .ToImmutableList(); await OutputPortraitModifiers(outputPath, charactersWithDNA, conversionDate, ck3ModFS); - var charactersFromIR = characters.Where(c => c.FromImperator) + var charactersFromIR = characters.AsValueEnumerable().Where(c => c.FromImperator) .OrderBy(c => c.Id).ToImmutableList(); - var charactersFromCK3 = characters.Except(charactersFromIR) + var charactersFromCK3 = characters.AsValueEnumerable().Except(charactersFromIR) .OrderBy(c => c.Id).ToImmutableList(); var sb = new StringBuilder(); @@ -104,7 +105,7 @@ private static async Task OutputCharactersDNA(string outputPath, IEnumerable<Cha } } - private static HashSet<string> GetValidAccessoryIDs(ModFilesystem ck3ModFS) { + private static FrozenSet<string> GetValidAccessoryIDs(ModFilesystem ck3ModFS) { Logger.Debug("Getting valid CK3 accessory IDs..."); var accessoryIDs = new ConcurrentHashSet<string>(); @@ -117,7 +118,7 @@ private static HashSet<string> GetValidAccessoryIDs(ModFilesystem ck3ModFS) { accessoryFilesParser.IgnoreAndLogUnregisteredItems(); accessoryFilesParser.ParseGameFolder("gfx/portraits/accessories", ck3ModFS, "txt", recursive: true, logFilePaths: false, parallel: true); - return accessoryIDs.ToHashSet(); + return accessoryIDs.ToFrozenSet(); } private static async Task OutputPortraitModifiers(string outputPath, IReadOnlyCollection<Character> charactersWithDNA, Date conversionDate, ModFilesystem ck3ModFS) { @@ -131,7 +132,7 @@ private static async Task OutputPortraitModifiers(string outputPath, IReadOnlyCo await using var output = FileHelper.OpenWriteWithRetries(portraitModifiersOutputPath, Encoding.UTF8); await OutputPortraitModifiersForGene("hairstyles", validAccessoryIDs, charactersWithDNA, output, conversionDate); - var malesWithBeards = charactersWithDNA + var malesWithBeards = charactersWithDNA.AsValueEnumerable() .Where(c => !c.Female && c.DNA!.AccessoryDNAValues.ContainsKey("beards")) .ToImmutableList(); await OutputPortraitModifiersForGene("beards", validAccessoryIDs, malesWithBeards, output, conversionDate); @@ -139,14 +140,14 @@ private static async Task OutputPortraitModifiers(string outputPath, IReadOnlyCo private static async Task OutputPortraitModifiersForGene( string geneName, - HashSet<string> validAccessoryIDs, + FrozenSet<string> validAccessoryIDs, IReadOnlyCollection<Character> charactersWithDNA, TextWriter output, Date conversionDate ) { var sb = new StringBuilder(); - var charactersByGeneValue = charactersWithDNA + var charactersByGeneValue = charactersWithDNA.AsValueEnumerable() .Where(c => c.DNA!.AccessoryDNAValues.ContainsKey(geneName)) .GroupBy(c => new { c.DNA!.AccessoryDNAValues[geneName].TemplateName, @@ -176,7 +177,7 @@ Date conversionDate string accessoryOrValueString = validAccessoryIDs.Contains(accessoryName) ? $"accessory = {accessoryName}" - : $"value = {grouping.First().DNA!.AccessoryDNAValues[geneName].SliderValueBetween0And1:0.####}"; + : $"value = {grouping.AsValueEnumerable().First().DNA!.AccessoryDNAValues[geneName].SliderValueBetween0And1:0.####}"; sb.AppendLine($"\t\t\t\t{accessoryOrValueString}"); sb.AppendLine("\t\t\t}"); sb.AppendLine("\t\t}"); diff --git a/ImperatorToCK3/Outputter/CoatOfArmsOutputter.cs b/ImperatorToCK3/Outputter/CoatOfArmsOutputter.cs index 01c0b891e..742082be6 100644 --- a/ImperatorToCK3/Outputter/CoatOfArmsOutputter.cs +++ b/ImperatorToCK3/Outputter/CoatOfArmsOutputter.cs @@ -5,8 +5,8 @@ using ImperatorToCK3.Mappers.CoA; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; +using ZLinq; namespace ImperatorToCK3.Outputter; public static class CoatOfArmsOutputter { @@ -35,7 +35,7 @@ internal static async Task OutputCoas(string outputModPath, Title.LandedTitles t } // Output CoAs for dynasties. - foreach (var dynasty in dynasties.Where(d => d.CoA is not null)) { + foreach (var dynasty in dynasties.AsValueEnumerable().Where(d => d.CoA is not null)) { sb.AppendLine($"{dynasty.Id}={dynasty.CoA}"); } diff --git a/ImperatorToCK3/Outputter/DynastiesOutputter.cs b/ImperatorToCK3/Outputter/DynastiesOutputter.cs index efd9048fd..9df8c1553 100644 --- a/ImperatorToCK3/Outputter/DynastiesOutputter.cs +++ b/ImperatorToCK3/Outputter/DynastiesOutputter.cs @@ -3,9 +3,9 @@ using ImperatorToCK3.CK3.Dynasties; using ImperatorToCK3.CommonUtils; using System.IO; -using System.Linq; using System.Text; using System.Threading.Tasks; +using ZLinq; namespace ImperatorToCK3.Outputter; @@ -14,7 +14,7 @@ public static async Task OutputDynasties(string outputModPath, DynastyCollection Logger.Info("Writing dynasties..."); var sb = new StringBuilder(); - foreach (var dynasty in dynasties.OrderBy(d => d.Id)) { + foreach (var dynasty in dynasties.AsValueEnumerable().OrderBy(d => d.Id)) { sb.AppendLine($"{dynasty.Id}={PDXSerializer.Serialize(dynasty, string.Empty)}"); } @@ -27,7 +27,7 @@ public static async Task OutputHouses(string outputModPath, HouseCollection hous Logger.Info("Writing dynasty houses..."); var sb = new StringBuilder(); - foreach (var house in houses.OrderBy(h => h.Id)) { + foreach (var house in houses.AsValueEnumerable().OrderBy(h => h.Id)) { sb.AppendLine($"{house.Id}={PDXSerializer.Serialize(house, string.Empty)}"); } diff --git a/ImperatorToCK3/Outputter/LocalizationOutputter.cs b/ImperatorToCK3/Outputter/LocalizationOutputter.cs index ec2e282f3..09f18e252 100644 --- a/ImperatorToCK3/Outputter/LocalizationOutputter.cs +++ b/ImperatorToCK3/Outputter/LocalizationOutputter.cs @@ -3,8 +3,8 @@ using ImperatorToCK3.CommonUtils; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; +using ZLinq; namespace ImperatorToCK3.Outputter; internal static class LocalizationOutputter { @@ -44,7 +44,7 @@ private static void OutputFallbackLocForMissingSecondaryLanguageLoc(string baseL languageToLocLinesDict[language] = []; } - var allLocKeys = ck3LocDB.Select(locBlock => locBlock.Id).Distinct().ToArray(); + var allLocKeys = ck3LocDB.AsValueEnumerable().Select(locBlock => locBlock.Id).Distinct().ToArray(); foreach (var locKey in allLocKeys) { if (!ck3LocDB.HasKeyLocForLanguage(locKey, ConverterGlobals.PrimaryLanguage)) { diff --git a/ImperatorToCK3/Outputter/MenAtArmsOutputter.cs b/ImperatorToCK3/Outputter/MenAtArmsOutputter.cs index aebe9010e..4fb3e605c 100644 --- a/ImperatorToCK3/Outputter/MenAtArmsOutputter.cs +++ b/ImperatorToCK3/Outputter/MenAtArmsOutputter.cs @@ -35,7 +35,7 @@ private static void OutputHiddenEvent(string outputModName, IEnumerable<Characte sb.AppendLine("}"); - var outputPath = Path.Combine("output", outputModName, "events", "irtock3_hidden_events.txt"); + var outputPath = Path.Combine("output", outputModName, "events/irtock3_hidden_events.txt"); using var output = FileHelper.OpenWriteWithRetries(outputPath, System.Text.Encoding.UTF8); output.Write(sb.ToString()); } diff --git a/ImperatorToCK3/Outputter/NamedColorsOutputter.cs b/ImperatorToCK3/Outputter/NamedColorsOutputter.cs index 331b7ef0d..ed4396302 100644 --- a/ImperatorToCK3/Outputter/NamedColorsOutputter.cs +++ b/ImperatorToCK3/Outputter/NamedColorsOutputter.cs @@ -2,9 +2,9 @@ using commonItems.Colors; using ImperatorToCK3.CommonUtils; using System.IO; -using System.Linq; using System.Text; using System.Threading.Tasks; +using ZLinq; namespace ImperatorToCK3.Outputter; @@ -16,7 +16,8 @@ public static class NamedColorsOutputter { /// <param name="imperatorNamedColors"></param> /// <param name="ck3NamedColors"></param> public static async Task OutputNamedColors(string outputModPath, NamedColorCollection imperatorNamedColors, NamedColorCollection ck3NamedColors) { - var diff = imperatorNamedColors.Where(colorPair => !ck3NamedColors.ContainsKey(colorPair.Key)) + var diff = imperatorNamedColors.AsValueEnumerable() + .Where(colorPair => !ck3NamedColors.ContainsKey(colorPair.Key)) .ToArray(); if (diff.Length == 0) { return; @@ -32,7 +33,7 @@ public static async Task OutputNamedColors(string outputModPath, NamedColorColle sb.AppendLine("}"); - var outputPath = Path.Combine(outputModPath, "common", "named_colors", "IRtoCK3_colors_from_Imperator.txt"); + var outputPath = Path.Combine(outputModPath, "common/named_colors/IRtoCK3_colors_from_Imperator.txt"); await using var output = FileHelper.OpenWriteWithRetries(outputPath, Encoding.UTF8); await output.WriteAsync(sb.ToString()); diff --git a/ImperatorToCK3/Outputter/ProvincesOutputter.cs b/ImperatorToCK3/Outputter/ProvincesOutputter.cs index 189033b11..bf983da6e 100644 --- a/ImperatorToCK3/Outputter/ProvincesOutputter.cs +++ b/ImperatorToCK3/Outputter/ProvincesOutputter.cs @@ -3,10 +3,10 @@ using ImperatorToCK3.CK3.Titles; using ImperatorToCK3.CommonUtils; using Open.Collections; -using System.Collections.Generic; +using System.Collections.Frozen; using System.IO; -using System.Linq; using System.Threading.Tasks; +using ZLinq; namespace ImperatorToCK3.Outputter; @@ -18,11 +18,11 @@ Title.LandedTitles titles ) { Logger.Info("Writing provinces..."); - HashSet<ulong> countyCapitalProvinceIds = titles.Counties + FrozenSet<ulong> countyCapitalProvinceIds = titles.Counties.AsValueEnumerable() .Select(title => title.CapitalBaronyProvinceId) .Where(id => id is not null) .Select(id => id!.Value) - .ToHashSet(); + .ToFrozenSet(); // Output provinces to files named after their de jure kingdoms. var alreadyOutputtedProvinces = new ConcurrentHashSet<ulong>();