diff --git a/ImperatorToCK3.UnitTests/CK3/Characters/CharacterCollectionTests.cs b/ImperatorToCK3.UnitTests/CK3/Characters/CharacterCollectionTests.cs index fdab49784..3b6afe952 100644 --- a/ImperatorToCK3.UnitTests/CK3/Characters/CharacterCollectionTests.cs +++ b/ImperatorToCK3.UnitTests/CK3/Characters/CharacterCollectionTests.cs @@ -25,6 +25,7 @@ using ImperatorToCK3.Mappers.TagTitle; using ImperatorToCK3.Mappers.Trait; using ImperatorToCK3.UnitTests.TestHelpers; +using Open.Collections; using System.Collections.Generic; using System.Linq; using Xunit; @@ -60,8 +61,8 @@ static CharacterCollectionTests() { areas.LoadAreas(irModFS, irProvinces); irRegionMapper = new ImperatorRegionMapper(areas, irMapData); irRegionMapper.LoadRegions(irModFS, colorFactory); - - var ck3ModFlags = new List(); + + var ck3ModFlags = new System.Collections.Generic.OrderedDictionary(); cultures = new CultureCollection(colorFactory, new PillarCollection(colorFactory, ck3ModFlags), ck3ModFlags); } diff --git a/ImperatorToCK3.UnitTests/CK3/Cultures/CultureCollectionTests.cs b/ImperatorToCK3.UnitTests/CK3/Cultures/CultureCollectionTests.cs index 95c4bc8a9..818cb6ca0 100644 --- a/ImperatorToCK3.UnitTests/CK3/Cultures/CultureCollectionTests.cs +++ b/ImperatorToCK3.UnitTests/CK3/Cultures/CultureCollectionTests.cs @@ -14,7 +14,7 @@ public class CultureCollectionTests { private static readonly ModFilesystem ck3ModFS = new("TestFiles/CK3/game", Array.Empty()); private static readonly PillarCollection pillars; private static readonly ColorFactory colorFactory = new(); - private static readonly List ck3ModFlags = []; + private static readonly OrderedDictionary ck3ModFlags = []; static CultureCollectionTests() { pillars = new PillarCollection(colorFactory, []) { diff --git a/ImperatorToCK3.UnitTests/CK3/ParserExtensionsTests.cs b/ImperatorToCK3.UnitTests/CK3/ParserExtensionsTests.cs new file mode 100644 index 000000000..5c3ef39e5 --- /dev/null +++ b/ImperatorToCK3.UnitTests/CK3/ParserExtensionsTests.cs @@ -0,0 +1,193 @@ +using commonItems; +using ImperatorToCK3.CK3; +using System.Collections.Generic; +using Xunit; + +namespace ImperatorToCK3.UnitTests.CK3; + +[Collection("Sequential")] +[CollectionDefinition("Sequential", DisableParallelization = true)] +public class ParserExtensionsTests { + [Theory] + [InlineData(true, false, false, 0, 0)] + [InlineData(false, true, false, 1, 1)] + [InlineData(false, false, true, 2, 0)] + [InlineData(true, true, false, 0, 1)] + public void CorrectModDependentBranchesAreUsed(bool wtwsms, bool tfe, bool vanilla, int expectedValue1, + int expectedValue2) { + var ck3ModFlags = new Dictionary {["wtwsms"] = wtwsms, ["tfe"] = tfe, ["vanilla"] = vanilla,}; + + var blocReader = new BufferedReader( + """ + MOD_DEPENDENT = { + IF @wtwsms = { # Interpolated expression without brackets is valid, therefor should be supported. + value1 = 0 + } ELSE_IF tfe = {# Simple mod flag string should be supported as well. + value1 = 1 + } ELSE = { + value1 = 2 + } + + IF @[wtwsms|vanilla|tfe] = { # Logical OR, example of more complex interpolated expression. + value2 = 0 + } + IF @[tfe] = { # Will override the previous value2. + value2 = 1 + } + } + """); + + int? value1 = null; + int? value2 = null; + var parser = new Parser(); + parser.RegisterModDependentBloc(ck3ModFlags); + parser.RegisterKeyword("value1", reader => value1 = reader.GetInt()); + parser.RegisterKeyword("value2", reader => value2 = reader.GetInt()); + parser.ParseStream(blocReader); + + Assert.Equal(expectedValue1, value1); + Assert.Equal(expectedValue2, value2); + } + + [Fact] + public void ExceptionIsThrownWhenUnknownModFlagIsEncounteredInInterpolatedExpression() { + var reader1 = new BufferedReader( + """ + MOD_DEPENDENT = { + IF @[unknown_mod] = { # Undefined mod flag in interpolated expression. + value = 1 + } ELSE = { + value = 2 + } + } + """); + + int? value = null; + Dictionary modFlags = new(); + + var parser1 = new Parser(); + parser1.RegisterModDependentBloc(modFlags); + parser1.RegisterKeyword("value", reader => value = reader.GetInt()); + Assert.Throws(() => parser1.ParseStream(reader1)); + Assert.Null(value); + } + + [Fact] + public void VariableConditionResolvesToFalseWhenVariableIsNotDefined() { + var reader1 = new BufferedReader( + """ + MOD_DEPENDENT = { + IF @unknown_variable = { # Undefined variable in interpolated expression. + value = 1 + } ELSE = { + value = 2 + } + } + """); + + int? value = null; + Dictionary modFlags = new(); + + var parser1 = new Parser(); + parser1.RegisterModDependentBloc(modFlags); + parser1.RegisterKeyword("value", reader => value = reader.GetInt()); + parser1.ParseStream(reader1); // Should not throw. + Assert.Equal(2, value); + } + + [Fact] + public void ElseIfAfterElseIsIgnored() { + Dictionary ck3ModFlags = new() {{"wtwsms", false}, {"tfe", true}, {"vanilla", false},}; + + var blocReader = new BufferedReader( + """ + MOD_DEPENDENT = { + IF wtwsms = { + value = 0 + } ELSE = { + value = 2 + } ELSE_IF tfe = { # Should be ignored, even if the condition is true. + value = 3 + } + } + """); + + int? value = null; + var parser = new Parser(); + parser.RegisterModDependentBloc(ck3ModFlags); + parser.RegisterKeyword("value", reader => value = reader.GetInt()); + parser.ParseStream(blocReader); + + Assert.Equal(2, value); + } + + [Fact] + public void ElseAfterElseIsIgnored() { + Dictionary ck3ModFlags = new() {{"wtwsms", false}, {"tfe", true}, {"vanilla", false},}; + + var blocReader = new BufferedReader( + """ + MOD_DEPENDENT = { + IF wtwsms = { + value = 0 + } ELSE = { + value = 2 + } ELSE = { + value = 3 + } + } + """); + + int? value = null; + var parser = new Parser(); + parser.RegisterModDependentBloc(ck3ModFlags); + parser.RegisterKeyword("value", reader => value = reader.GetInt()); + parser.ParseStream(blocReader); + + Assert.Equal(2, value); + } + + [Fact] + public void ElseIfWithoutPrecedingIfIsIgnored() { + Dictionary ck3ModFlags = new() {{"wtwsms", false}, {"tfe", true}, {"vanilla", false},}; + + var blocReader = new BufferedReader( + """ + MOD_DEPENDENT = { + ELSE_IF tfe = { # Should be ignored, as there is no IF before it. + value = 3 + } + } + """); + + int? value = null; + var parser = new Parser(); + parser.RegisterModDependentBloc(ck3ModFlags); + parser.RegisterKeyword("value", reader => value = reader.GetInt()); + parser.ParseStream(blocReader); + + Assert.Null(value); + } + + [Fact] + public void ElseWithoutPrecedingIfIsIgnored() { + Dictionary ck3ModFlags = new() {{"wtwsms", false}, {"tfe", true}, {"vanilla", false},}; + + var blocReader = new BufferedReader( + """ + MOD_DEPENDENT = { + ELSE = { # Should be ignored, as there is no IF before it. + value = 3 + } + } + """); + + int? value = null; + var parser = new Parser(); + parser.RegisterModDependentBloc(ck3ModFlags); + parser.RegisterKeyword("value", reader => value = reader.GetInt()); + parser.ParseStream(blocReader); + + Assert.Null(value); + } +} \ No newline at end of file diff --git a/ImperatorToCK3.UnitTests/CK3/Titles/LandedTitlesTests.cs b/ImperatorToCK3.UnitTests/CK3/Titles/LandedTitlesTests.cs index 2c368a40e..ad785dad8 100644 --- a/ImperatorToCK3.UnitTests/CK3/Titles/LandedTitlesTests.cs +++ b/ImperatorToCK3.UnitTests/CK3/Titles/LandedTitlesTests.cs @@ -61,7 +61,7 @@ static LandedTitlesTests() { public LandedTitlesTests() { var colorFactory = new ColorFactory(); - var ck3ModFlags = Array.Empty(); + var ck3ModFlags = new OrderedDictionary(); PillarCollection pillars = new(colorFactory, ck3ModFlags); cultures = new CultureCollection(colorFactory, pillars, ck3ModFlags); } diff --git a/ImperatorToCK3.UnitTests/Mappers/TagTitle/TagTitleMapperTests.cs b/ImperatorToCK3.UnitTests/Mappers/TagTitle/TagTitleMapperTests.cs index 26a462da8..c210ce006 100644 --- a/ImperatorToCK3.UnitTests/Mappers/TagTitle/TagTitleMapperTests.cs +++ b/ImperatorToCK3.UnitTests/Mappers/TagTitle/TagTitleMapperTests.cs @@ -44,7 +44,7 @@ public class TagTitleMapperTests { private static readonly ColorFactory ColorFactory = new(); static TagTitleMapperTests() { - var ck3ModFlags = new List(); + var ck3ModFlags = new OrderedDictionary(); var pillars = new PillarCollection(ColorFactory, ck3ModFlags); cultures = new CultureCollection(ColorFactory, pillars, ck3ModFlags); } diff --git a/ImperatorToCK3.UnitTests/Outputter/CoatOfArmsOutputterTests.cs b/ImperatorToCK3.UnitTests/Outputter/CoatOfArmsOutputterTests.cs index 2b09660ce..936fde69a 100644 --- a/ImperatorToCK3.UnitTests/Outputter/CoatOfArmsOutputterTests.cs +++ b/ImperatorToCK3.UnitTests/Outputter/CoatOfArmsOutputterTests.cs @@ -56,7 +56,7 @@ public async Task CoaIsOutputtedForCountryWithFlagSet() { var ck3Religions = new ReligionCollection(titles); var ck3RegionMapper = new CK3RegionMapper(); - var ck3ModFlags = new List(); + var ck3ModFlags = new OrderedDictionary(); titles.ImportImperatorCountries(countries, Array.Empty(), new TagTitleMapper(), @@ -102,7 +102,7 @@ public async Task CoaIsNotOutputtedForCountryWithoutFlagSet() { var ck3Religions = new ReligionCollection(titles); var ck3RegionMapper = new CK3RegionMapper(); - var ck3ModFlags = new List(); + var ck3ModFlags = new OrderedDictionary(); titles.ImportImperatorCountries(countries, Array.Empty(), new TagTitleMapper(), diff --git a/ImperatorToCK3.UnitTests/Outputter/DynastiesOutputterTests.cs b/ImperatorToCK3.UnitTests/Outputter/DynastiesOutputterTests.cs index f83c8cae6..cbb1b03b6 100644 --- a/ImperatorToCK3.UnitTests/Outputter/DynastiesOutputterTests.cs +++ b/ImperatorToCK3.UnitTests/Outputter/DynastiesOutputterTests.cs @@ -14,6 +14,7 @@ using ImperatorToCK3.Outputter; using ImperatorToCK3.UnitTests.TestHelpers; using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Xunit; @@ -36,7 +37,7 @@ public async Task DynastiesAreOutputted() { irRegionMapper.LoadRegions(irModFS, new ColorFactory()); var colorFactory = new ColorFactory(); irRegionMapper.LoadRegions(irModFS, colorFactory); - var ck3ModFlags = Array.Empty(); + var ck3ModFlags = new OrderedDictionary(); CultureMapper cultureMapper = new(irRegionMapper, new CK3RegionMapper(), new CultureCollection(colorFactory, new PillarCollection(colorFactory, ck3ModFlags), ck3ModFlags)); var characters = new CharacterCollection(); diff --git a/ImperatorToCK3.UnitTests/TestHelpers/TestCK3CultureCollection.cs b/ImperatorToCK3.UnitTests/TestHelpers/TestCK3CultureCollection.cs index efd2bf5f2..1a67b0153 100644 --- a/ImperatorToCK3.UnitTests/TestHelpers/TestCK3CultureCollection.cs +++ b/ImperatorToCK3.UnitTests/TestHelpers/TestCK3CultureCollection.cs @@ -7,7 +7,7 @@ namespace ImperatorToCK3.UnitTests.TestHelpers; public class TestCK3CultureCollection() : CultureCollection(colorFactory, new PillarCollection(colorFactory, ck3ModFlags), ck3ModFlags) { private static readonly ColorFactory colorFactory = new(); - private static readonly List ck3ModFlags = []; + private static readonly OrderedDictionary ck3ModFlags = []; public void LoadConverterPillars(string filePath) { PillarCollection.LoadConverterPillars(filePath); diff --git a/ImperatorToCK3.sln.DotSettings b/ImperatorToCK3.sln.DotSettings index da6614b36..752183713 100644 --- a/ImperatorToCK3.sln.DotSettings +++ b/ImperatorToCK3.sln.DotSettings @@ -3,6 +3,7 @@ AI BC BOM + CCU CK DB DDS diff --git a/ImperatorToCK3/CK3/Cultures/CultureCollection.cs b/ImperatorToCK3/CK3/Cultures/CultureCollection.cs index 04cde9fbb..a67d4c651 100644 --- a/ImperatorToCK3/CK3/Cultures/CultureCollection.cs +++ b/ImperatorToCK3/CK3/Cultures/CultureCollection.cs @@ -17,13 +17,14 @@ namespace ImperatorToCK3.CK3.Cultures; public class CultureCollection : IdObjectCollection { - public CultureCollection(ColorFactory colorFactory, PillarCollection pillarCollection, ICollection ck3ModFlags) { + public CultureCollection(ColorFactory colorFactory, PillarCollection pillarCollection, OrderedDictionary ck3ModFlags) { this.PillarCollection = pillarCollection; InitCultureDataParser(colorFactory, ck3ModFlags); } - private void InitCultureDataParser(ColorFactory colorFactory, ICollection ck3ModFlags) { + private void InitCultureDataParser(ColorFactory colorFactory, OrderedDictionary ck3ModFlags) { cultureDataParser.RegisterKeyword("INVALIDATED_BY", reader => LoadInvalidatingCultureIds(ck3ModFlags, reader)); + cultureDataParser.RegisterModDependentBloc(ck3ModFlags); cultureDataParser.RegisterKeyword("color", reader => { try { cultureData.Color = colorFactory.GetColor(reader); @@ -38,14 +39,14 @@ private void InitCultureDataParser(ColorFactory colorFactory, ICollection { var languageId = reader.GetString(); cultureData.Language = PillarCollection.GetLanguageForId(languageId); if (cultureData.Language is null) { - Logger.Warn($"Found unrecognized language when parsing cultures: {languageId}"); + Logger.Debug($"Found unrecognized language when parsing cultures: {languageId}"); } }); cultureDataParser.RegisterKeyword("traditions", reader => { @@ -65,7 +66,7 @@ private void InitCultureDataParser(ColorFactory colorFactory, ICollection ck3ModFlags, BufferedReader reader) { + private void LoadInvalidatingCultureIds(OrderedDictionary ck3ModFlags, BufferedReader reader) { var cultureIdsPerModFlagParser = new Parser(); if (ck3ModFlags.Count == 0) { @@ -73,8 +74,8 @@ private void LoadInvalidatingCultureIds(ICollection ck3ModFlags, Buffere cultureData.InvalidatingCultureIds = modCultureIdsReader.GetStrings(); }); } else { - foreach (var modFlag in ck3ModFlags) { - cultureIdsPerModFlagParser.RegisterKeyword(modFlag, modCultureIdsReader => { + foreach (var modFlag in ck3ModFlags.Where(f => f.Value)) { + cultureIdsPerModFlagParser.RegisterKeyword(modFlag.Key, modCultureIdsReader => { cultureData.InvalidatingCultureIds = modCultureIdsReader.GetStrings(); }); } @@ -142,11 +143,11 @@ private void ValidateAndLoadCultures(OrderedDictionary cult Logger.Debug($"Loading optional culture {cultureId}..."); } if (data.Heritage is null) { - Logger.Warn($"Culture {cultureId} has no heritage defined! Skipping."); + Logger.Warn($"Culture {cultureId} has no valid heritage defined! Skipping."); continue; } if (data.Language is null) { - Logger.Warn($"Culture {cultureId} has no language defined! Skipping."); + Logger.Warn($"Culture {cultureId} has no valid language defined! Skipping."); continue; } if (data.NameLists.Count == 0) { @@ -158,6 +159,7 @@ private void ValidateAndLoadCultures(OrderedDictionary cult var color = new ColorHash().Rgb(cultureId); data.Color = new Color(color.R, color.G, color.B); } + AddOrReplace(new Culture(cultureId, data)); } } diff --git a/ImperatorToCK3/CK3/Cultures/PillarCollection.cs b/ImperatorToCK3/CK3/Cultures/PillarCollection.cs index d349072a8..a1223e6a2 100644 --- a/ImperatorToCK3/CK3/Cultures/PillarCollection.cs +++ b/ImperatorToCK3/CK3/Cultures/PillarCollection.cs @@ -12,7 +12,7 @@ namespace ImperatorToCK3.CK3.Cultures; public sealed class PillarCollection : IdObjectCollection { private readonly Dictionary mergedPillarsDict = []; - public PillarCollection(ColorFactory colorFactory, ICollection ck3ModFlags) { + public PillarCollection(ColorFactory colorFactory, OrderedDictionary ck3ModFlags) { InitPillarDataParser(colorFactory, ck3ModFlags); } @@ -73,7 +73,8 @@ private void LoadPillar(string pillarId, BufferedReader pillarReader) { AddOrReplace(new Pillar(pillarId, pillarData)); } - private void InitPillarDataParser(ColorFactory colorFactory, ICollection ck3ModFlags) { + private void InitPillarDataParser(ColorFactory colorFactory, OrderedDictionary ck3ModFlags) { + pillarDataParser.RegisterModDependentBloc(ck3ModFlags); pillarDataParser.RegisterKeyword("REPLACED_BY", reader => LoadInvalidatingPillarIds(ck3ModFlags, reader)); pillarDataParser.RegisterKeyword("type", reader => { pillarData.Type = reader.GetString(); @@ -91,7 +92,7 @@ private void InitPillarDataParser(ColorFactory colorFactory, ICollection pillarDataParser.IgnoreAndLogUnregisteredItems(); } - private void LoadInvalidatingPillarIds(ICollection ck3ModFlags, BufferedReader reader) { + private void LoadInvalidatingPillarIds(OrderedDictionary ck3ModFlags, BufferedReader reader) { var pillarIdsPerModFlagParser = new Parser(); if (ck3ModFlags.Count == 0) { @@ -99,8 +100,8 @@ private void LoadInvalidatingPillarIds(ICollection ck3ModFlags, Buffered pillarData.InvalidatingPillarIds = modPillarIdsReader.GetStrings(); }); } else { - foreach (var modFlag in ck3ModFlags) { - pillarIdsPerModFlagParser.RegisterKeyword(modFlag, modPillarIdsReader => { + foreach (var modFlag in ck3ModFlags.Where(f => f.Value)) { + pillarIdsPerModFlagParser.RegisterKeyword(modFlag.Key, modPillarIdsReader => { pillarData.InvalidatingPillarIds = modPillarIdsReader.GetStrings(); }); } diff --git a/ImperatorToCK3/CK3/ParserExtensions.cs b/ImperatorToCK3/CK3/ParserExtensions.cs new file mode 100644 index 000000000..18abe857e --- /dev/null +++ b/ImperatorToCK3/CK3/ParserExtensions.cs @@ -0,0 +1,87 @@ +using commonItems; +using System; +using System.Collections.Generic; + +namespace ImperatorToCK3.CK3; + +public static class ParserExtensions { + private static bool GetConditionValue(BufferedReader reader, IDictionary ck3ModFlags) { + var conditionLexeme = Parser.GetNextLexeme(reader); + if (CommonRegexes.Variable.IsMatch(conditionLexeme)) { + var value = reader.ResolveVariable(conditionLexeme); + if (value is null) { + return false; + } + if (value is bool boolValue) { + return boolValue; + } + return Convert.ToBoolean(value); + } else if (CommonRegexes.InterpolatedExpression.IsMatch(conditionLexeme)) { + // Interpolated expression. + var value = reader.EvaluateExpression(conditionLexeme); + if (value is bool boolValue) { + return boolValue; + } + return Convert.ToBoolean(value); + } else { + // Otherwise the token is expected to be a mod flag name. + return ck3ModFlags[conditionLexeme]; + } + } + public static void RegisterModDependentBloc(this Parser parser, IDictionary ck3ModFlags) { + parser.RegisterKeyword("MOD_DEPENDENT", blocReader => { + // elseMode changes to true when IF condition is false. + // Changes back to false when an ELSE_IF or ELSE block is entered. + // Also changes to false when an IF block is encountered. + bool elseMode = false; + + foreach (var (modFlagName, value) in ck3ModFlags) { + blocReader.Variables[modFlagName] = value; + } + + var modDependentParser = new Parser(); + modDependentParser.RegisterKeyword("IF", reader => { + bool conditionValue = GetConditionValue(reader, ck3ModFlags); + if (!conditionValue) { + elseMode = true; + ParserHelpers.IgnoreItem(reader); + } else { + elseMode = false; + parser.ParseStream(reader); + } + }); + + modDependentParser.RegisterKeyword("ELSE_IF", reader => { + // If not in elseMode, skip the block. + if (!elseMode) { + // Skip the condition first. + Parser.GetNextLexeme(reader); + // Skip the block. + ParserHelpers.IgnoreItem(reader); + } else { + bool conditionValue = GetConditionValue(reader, ck3ModFlags); + if (!conditionValue) { + // If condition is false, skip the block. + elseMode = true; + ParserHelpers.IgnoreItem(reader); + } else { + elseMode = false; + parser.ParseStream(reader); + } + } + }); + + modDependentParser.RegisterKeyword("ELSE", reader => { + // If not in elseMode, skip the block. + if (!elseMode) { + ParserHelpers.IgnoreItem(reader); + } else { + elseMode = false; // There should be no more ELSE_IF or ELSE blocks after ELSE. + parser.ParseStream(reader); + } + }); + modDependentParser.IgnoreAndLogUnregisteredItems(); + modDependentParser.ParseStream(blocReader); + }); + } +} \ No newline at end of file diff --git a/ImperatorToCK3/CK3/Religions/HolySite.cs b/ImperatorToCK3/CK3/Religions/HolySite.cs index b3426b555..6e0b8bfb8 100644 --- a/ImperatorToCK3/CK3/Religions/HolySite.cs +++ b/ImperatorToCK3/CK3/Religions/HolySite.cs @@ -5,7 +5,6 @@ using ImperatorToCK3.CK3.Titles; using ImperatorToCK3.Mappers.HolySiteEffect; using System.Collections.Generic; -using System.Linq; namespace ImperatorToCK3.CK3.Religions; diff --git a/ImperatorToCK3/CK3/World.cs b/ImperatorToCK3/CK3/World.cs index 0472774cf..d5da96c63 100644 --- a/ImperatorToCK3/CK3/World.cs +++ b/ImperatorToCK3/CK3/World.cs @@ -96,11 +96,8 @@ public World(Imperator.World impWorld, Configuration config) { ModLoader modLoader = new(); modLoader.LoadMods(Directory.GetParent(config.CK3ModsPath)!.FullName, incomingCK3Mods); LoadedMods = modLoader.UsableMods.ToOrderedSet(); - var tfeMod = LoadedMods.FirstOrDefault(m => m.Name.StartsWith("The Fallen Eagle", StringComparison.Ordinal)); - config.FallenEagleEnabled = tfeMod is not null; - if (config.FallenEagleEnabled) { - Logger.Info($"TFE detected: {tfeMod!.Name}"); - } + config.DetectSpecificCK3Mods(LoadedMods); + // Include a fake mod pointing to blankMod. LoadedMods.Add(new Mod("blankMod", "blankMod/output")); ModFS = new ModFilesystem(Path.Combine(config.CK3Path, "game"), LoadedMods); diff --git a/ImperatorToCK3/Configuration.cs b/ImperatorToCK3/Configuration.cs index 9cb7024e5..9bf0d38d4 100644 --- a/ImperatorToCK3/Configuration.cs +++ b/ImperatorToCK3/Configuration.cs @@ -1,10 +1,12 @@ using commonItems; using commonItems.Collections; +using commonItems.Mods; using ImperatorToCK3.Exceptions; using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; namespace ImperatorToCK3; @@ -27,6 +29,7 @@ public sealed class Configuration { public Date CK3BookmarkDate { get; set; } = new(0, 1, 1); public bool SkipDynamicCoAExtraction { get; set; } = false; public bool FallenEagleEnabled { get; set; } + public bool WhenTheWorldStoppedMakingSenseEnabled { get; set; } public Configuration() { } public Configuration(ConverterVersion converterVersion) { @@ -260,11 +263,28 @@ private void VerifyCK3Version(ConverterVersion converterVersion) { } } - public ICollection GetCK3ModFlags() { - var flags = new HashSet(); - if (FallenEagleEnabled) { - flags.Add("tfe"); + public void DetectSpecificCK3Mods(ICollection loadedMods) { + var tfeMod = loadedMods.FirstOrDefault(m => m.Name.StartsWith("The Fallen Eagle", StringComparison.Ordinal)); + if (tfeMod is not null) { + FallenEagleEnabled = true; + Logger.Info($"TFE detected: {tfeMod.Name}"); } + + var wtwsmsMod = loadedMods.FirstOrDefault(m => m.Name.StartsWith("When the World Stopped Making Sense", StringComparison.Ordinal)); + if (wtwsmsMod is not null) { + WhenTheWorldStoppedMakingSenseEnabled = true; + Logger.Info($"WtWSMS detected: {wtwsmsMod.Name}"); + } + } + + /// Returns a collection of CK3 mod flags with values based on the enabled mods. "vanilla" flag is set to true if no other flags are set. + public OrderedDictionary GetCK3ModFlags() { + var flags = new OrderedDictionary { + ["tfe"] = FallenEagleEnabled, + ["wtwsms"] = WhenTheWorldStoppedMakingSenseEnabled, + }; + + flags["vanilla"] = flags.Count(f => f.Value) == 0; return flags; } } \ No newline at end of file diff --git a/ImperatorToCK3/Data_Files/blankMod/output/common/named_colors/IRToCK3_culture_colors.txt b/ImperatorToCK3/Data_Files/blankMod/output/common/named_colors/IRToCK3_culture_colors.txt index abf167185..9daabacd9 100644 --- a/ImperatorToCK3/Data_Files/blankMod/output/common/named_colors/IRToCK3_culture_colors.txt +++ b/ImperatorToCK3/Data_Files/blankMod/output/common/named_colors/IRToCK3_culture_colors.txt @@ -5,4 +5,21 @@ colors = { arakanese = rgb { 230 37 82 } # based on d_arakan color shan = rgb { 255 157 0 } # based on orange color from this map: https://upload.wikimedia.org/wikipedia/commons/5/55/Taikadai-en1.png venetic_culture = { 193 117 241 } + + # fallback colors for WtWSMS + coptic = { 0.20 0.35 0.50 } # taken from the coptic culture color in WtWSMS + sakan = { 0.55 0.44 0.33 } # taken from the saka named color in WtWSMS + language_latin = hsv{ 0.000 1.000 0.600 } # taken from the roman culture color in WtWSMS + language_iberian = { 0.8 0.7 0.0 } # taken from romano_hispanic named color in WtWSMS + language_galloromance = { 0.39 0.702 0.412 } # taken from the romano_gallic named color in WtWSMS + language_britoromance = { 0.49 0.815 0.82 } # taken from the romano_british named color in WtWSMS + language_italoromance = { 0.94 0.45 0.55 } # taken from corsican named color in WtWSMS + language_afroromance = { 0.9 0.04 0.04 } # taken from the romano_african named color in WtWSMS + language_illyroromance = { 0.7 0.55 0.70 } # taken from the romano_illyrian named color in WtWSMS + language_rhaetoromance = { 0.4 0.4 0.5 } # taken from the romano_raetian named color in WtWSMS + language_pannonioromance = { 0.8 0.55 0.8 } # taken from the romano_pannonian named color in WtWSMS + language_easternromance = { 0.25 0.2 0.52 } # taken from the romano_dacian named color in WtWSMS + language_syrianromance = { 0.85 0.3 0.3 } # taken from the romano_aramean named color in WtWSMS + trojan = { 0.83 0.55 0.95 } # taken from the trojan named color in vanilla CK3 + ancient_egyptian = { 0.9 0.85 0.17 } # taken from the ancient_egyptian named color in vanilla CK3 } \ No newline at end of file diff --git a/ImperatorToCK3/Data_Files/configurables/ccu_language_parameters.txt b/ImperatorToCK3/Data_Files/configurables/ccu_language_parameters.txt new file mode 100644 index 000000000..641f918c8 --- /dev/null +++ b/ImperatorToCK3/Data_Files/configurables/ccu_language_parameters.txt @@ -0,0 +1,27 @@ +# This file contains the language families and language branches added by the converter +# when a CK3 mod that uses Community Culture Utility mechanics is detected. +# Currently the only such mod is "When the World Stopped Making Sense". + + +language_families = { + MOD_DEPENDENT = { + IF wtwsms = { + language_family_kra_dai + language_family_elamite_family + language_family_paleo_sardinian + language_family_tyrsenian + } + } +} + +language_branches = { + MOD_DEPENDENT = { + IF wtwsms = { + language_branch_palaungic + language_branch_tai + language_branch_nuragic + language_branch_elamite_group + language_branch_etruscan + } + } +} diff --git a/ImperatorToCK3/Data_Files/configurables/converter_cultures.txt b/ImperatorToCK3/Data_Files/configurables/converter_cultures.txt index 3ce730f28..2285af362 100644 --- a/ImperatorToCK3/Data_Files/configurables/converter_cultures.txt +++ b/ImperatorToCK3/Data_Files/configurables/converter_cultures.txt @@ -470,6 +470,7 @@ albanian = { ancient_egyptian = { # fallback in case some mod removes vanilla ancient_egyptian INVALIDATED_BY = { tfe = { ancient_egyptian kemetic } + wtwsms = { ancient_egyptian } vanilla = { ancient_egyptian kemetic } } @@ -509,7 +510,13 @@ numidian = { name_list = name_list_numidian heritage = heritage_berber - language = language_berber + MOD_DEPENDENT = { + IF wtwsms = { + language = language_northern_berber # Changed language_berber to language_northern_berber. + } ELSE = { + language = language_berber + } + } martial_custom = martial_custom_male_only ethos = ethos_stoic @@ -608,7 +615,13 @@ macedonian = { ethos = ethos_bellicose heritage = heritage_byzantine # IRToCK3: moved from heritage_ancient_greek - language = language_greek # IRToCK3: moved from language_ancient_greek + MOD_DEPENDENT = { + IF wtwsms = { + language = language_doric # https://en.wikipedia.org/wiki/Ancient_Macedonians: They spoke Ancient Macedonian, which is usually classified by scholars as a dialect of Northwest Doric Greek + } ELSE = { + language = language_greek # IRToCK3: moved from language_ancient_greek + } + } martial_custom = martial_custom_male_only traditions = { tradition_philosopher_culture diff --git a/ImperatorToCK3/Data_Files/configurables/cultural_pillars/00_language_venetic.txt b/ImperatorToCK3/Data_Files/configurables/cultural_pillars/00_language_venetic.txt index 34523da47..84a454a9c 100644 --- a/ImperatorToCK3/Data_Files/configurables/cultural_pillars/00_language_venetic.txt +++ b/ImperatorToCK3/Data_Files/configurables/cultural_pillars/00_language_venetic.txt @@ -11,27 +11,43 @@ language_venetic = { LANGUAGE = language_venetic } } - parameters = { - # language_group_romance = yes # Parameter for the Culture Expanded mod. - # language_family_indo_european = yes # Parameter for the Culture Expanded mod. + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_italic = yes + language_family_indo_european = yes + } + } } - - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_venetic } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_venetic } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_italic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_venetic } + multiply = 10 + } + } } - # ImperatorToCK3: below commented out, references script values probably from CCU mod - #else_if = { - # limit = { has_cultural_parameter = language_group_romance } - # multiply = same_language_group_choice_factor - #} - #else_if = { - # limit = { has_cultural_parameter = language_family_indo_european } - # multiply = same_language_family_choice_factor - #} - } color = venetic_culture diff --git a/ImperatorToCK3/Data_Files/configurables/cultural_pillars/IRToCK3_language.txt b/ImperatorToCK3/Data_Files/configurables/cultural_pillars/IRToCK3_language.txt index 55c53da24..83e181633 100644 --- a/ImperatorToCK3/Data_Files/configurables/cultural_pillars/IRToCK3_language.txt +++ b/ImperatorToCK3/Data_Files/configurables/cultural_pillars/IRToCK3_language.txt @@ -1,6 +1,9 @@ -language_gothic = { # https://en.wikipedia.org/wiki/East_Germanic_languages +# Add WtWSMS-specific stuff inside MOD_DEPENDENT = { IF @[wtwsms] = {} } blocks. + +language_gothic = { # https://en.wikipedia.org/wiki/East_Germanic_languages REPLACED_BY = { tfe = { language_gothic } + wtwsms = { language_gothic } vanilla = { language_gothic } } @@ -26,6 +29,15 @@ language_nuragic = { tfe = { language_nuragic } vanilla = { language_nuragic } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_nuragic = yes + language_family_paleo_sardinian = yes + } + } + } type = language is_shown = { @@ -33,11 +45,33 @@ language_nuragic = { LANGUAGE = language_nuragic } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_nuragic } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_nuragic } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_nuragic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_paleo_sardinian } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_nuragic } + multiply = 10 + } + } } } @@ -49,6 +83,15 @@ language_luwian = { tfe = { language_luwian } vanilla = { language_luwian } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_anatolian = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { @@ -56,11 +99,33 @@ language_luwian = { LANGUAGE = language_luwian } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_luwian } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_luwian } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_anatolian } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_luwian } + multiply = 10 + } + } } } @@ -72,6 +137,15 @@ language_lydian = { tfe = { language_lydian } vanilla = { language_lydian } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_anatolian = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { @@ -79,11 +153,33 @@ language_lydian = { LANGUAGE = language_lydian } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_lydian } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_lydian } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_anatolian } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_lydian } + multiply = 10 + } + } } } @@ -95,6 +191,15 @@ language_hittite = { tfe = { language_hittite } vanilla = { language_hittite } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_anatolian = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { @@ -102,11 +207,33 @@ language_hittite = { LANGUAGE = language_hittite } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_hittite } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_hittite } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_anatolian } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_hittite } + multiply = 10 + } + } } } @@ -118,6 +245,15 @@ language_phrygian = { tfe = { language_phrygian } vanilla = { language_phrygian } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_anatolian = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { @@ -125,11 +261,33 @@ language_phrygian = { LANGUAGE = language_phrygian } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_phrygian } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_phrygian } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_anatolian } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_phrygian } + multiply = 10 + } + } } } @@ -139,6 +297,7 @@ language_phrygian = { language_albanian = { REPLACED_BY = { tfe = { language_albanian } + wtwsms = { language_albanian } vanilla = { language_albanian } } @@ -162,7 +321,8 @@ language_albanian = { language_kemetic = { REPLACED_BY = { tfe = { language_kemetic } - vanilla = { language_kemetic } # TODO: replace with vanilla Ancient Egyptian ID + wtwsms = { language_kemetic } + vanilla = { language_egyptian } } type = language @@ -185,6 +345,7 @@ language_kemetic = { language_aramaic = { REPLACED_BY = { tfe = { language_aramaic } + wtwsms = { language_aramaic } vanilla = { language_aramaic } } @@ -210,6 +371,15 @@ language_babylonian = { tfe = { language_babylonian } vanilla = { language_babylonian } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_semitic = yes + language_family_afro_asiatic = yes + } + } + } type = language is_shown = { @@ -217,11 +387,33 @@ language_babylonian = { LANGUAGE = language_babylonian } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_babylonian } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_babylonian } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_semitic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_afro_asiatic } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_babylonian } + multiply = 10 + } + } } } @@ -231,6 +423,7 @@ language_babylonian = { language_gaulish = { REPLACED_BY = { tfe = { language_gaulish } + wtwsms = { language_gaulish } vanilla = { language_gaulish } } @@ -254,6 +447,7 @@ language_gaulish = { language_hebrew = { REPLACED_BY = { tfe = { language_hebrew } + wtwsms = { language_israelite } vanilla = { language_hebrew } } @@ -279,6 +473,15 @@ language_oscan = { tfe = { language_oscan } vanilla = { language_oscan } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_italic = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { @@ -286,14 +489,36 @@ language_oscan = { LANGUAGE = language_oscan } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_oscan } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_oscan } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_italic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_oscan } + multiply = 10 + } + } } } - + color = { 117 131 50 } } @@ -302,6 +527,15 @@ language_samnite = { tfe = { language_samnite } vanilla = { language_samnite } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_italic = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { @@ -309,11 +543,33 @@ language_samnite = { LANGUAGE = language_samnite } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_samnite } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_samnite } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_italic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_samnite } + multiply = 10 + } + } } } @@ -325,6 +581,15 @@ language_ligustic = { tfe = { language_ligustic } vanilla = { language_ligustic } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_italic = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { @@ -332,11 +597,33 @@ language_ligustic = { LANGUAGE = language_ligustic } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_ligustic } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_ligustic } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_italic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { # for conversions without WtWSMS + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_ligustic } + multiply = 10 + } + } } } @@ -348,6 +635,15 @@ language_messapic = { tfe = { language_messapic } vanilla = { language_messapic } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_italic = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { @@ -355,14 +651,36 @@ language_messapic = { LANGUAGE = language_messapic } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_messapic } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_messapic } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_italic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_messapic } + multiply = 10 + } + } } } - + color = { 5 52 113 } } @@ -371,6 +689,15 @@ language_sardonian = { tfe = { language_sardonian } vanilla = { language_sardonian } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_italic = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { @@ -378,11 +705,33 @@ language_sardonian = { LANGUAGE = language_sardonian } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_sardonian } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_sardonian } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_italic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_sardonian } + multiply = 10 + } + } } } @@ -394,6 +743,15 @@ language_siculian = { tfe = { language_siculian } vanilla = { language_siculian } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_italic = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { @@ -401,11 +759,33 @@ language_siculian = { LANGUAGE = language_siculian } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_siculian } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_siculian } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_italic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_siculian } + multiply = 10 + } + } } } @@ -415,6 +795,7 @@ language_siculian = { language_hunnic = { REPLACED_BY = { tfe = { language_hunnic } + wtwsms = { language_hunnic } vanilla = { language_hunnic } } @@ -438,6 +819,7 @@ language_hunnic = { language_punic = { REPLACED_BY = { tfe = { language_punic } + wtwsms = { language_punic } vanilla = { language_punic } } @@ -458,23 +840,59 @@ language_punic = { color = { 74 174 85 } } -language_breathanach = { +language_breathanach = { # language for Romano-Celtic hybrids in the British Isles REPLACED_BY = { tfe = { language_breathanach } vanilla = { language_breathanach } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_group_romance = yes + language_branch_italic = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { language_is_shown_trigger = { - LANGUAGE = language_breathanach + LANGUAGE = language_breathanach } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_breathanach } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_breathanach } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_group_romance } + multiply = same_language_group_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_branch_italic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_breathanach } + multiply = 10 + } + } } } @@ -486,18 +904,54 @@ language_laessin = { tfe = { language_laessin } vanilla = { language_laessin } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_group_romance = yes + language_branch_italic = yes + language_family_indo_european = yes + } + } + } type = language is_shown = { language_is_shown_trigger = { - LANGUAGE = language_laessin + LANGUAGE = language_laessin } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_laessin } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_laessin } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_group_romance } + multiply = same_language_group_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_branch_italic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_indo_european } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_laessin } + multiply = 10 + } + } } } @@ -507,13 +961,14 @@ language_laessin = { language_thracian = { REPLACED_BY = { tfe = { language_thracian } + wtwsms = { language_thracian } vanilla = { language_thracian } } type = language is_shown = { language_is_shown_trigger = { - LANGUAGE = language_thracian + LANGUAGE = language_thracian } } ai_will_do = { @@ -530,13 +985,14 @@ language_thracian = { language_dacian = { REPLACED_BY = { tfe = { language_dacian } + wtwsms = { language_dacian } vanilla = { language_dacian } } type = language is_shown = { language_is_shown_trigger = { - LANGUAGE = language_dacian + LANGUAGE = language_dacian } } ai_will_do = { @@ -555,6 +1011,15 @@ language_etruscan = { tfe = { language_etruscan } vanilla = { language_etruscan } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_etruscan = yes + language_family_tyrsenian = yes + } + } + } type = language is_shown = { @@ -562,11 +1027,33 @@ language_etruscan = { LANGUAGE = language_etruscan } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_etruscan } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_etruscan } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_etruscan } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_tyrsenian } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_etruscan } + multiply = 10 + } + } } } @@ -578,18 +1065,49 @@ language_elamite = { tfe = { language_elamite } vanilla = { language_elamite } } - + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_elamite_group = yes + language_family_elamite_family = yes + } + } + } + type = language is_shown = { language_is_shown_trigger = { - LANGUAGE = language_elamite + LANGUAGE = language_elamite } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_elamite } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_elamite } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_elamite_group } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_elamite_family } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_elamite } + multiply = 10 + } + } } } @@ -601,6 +1119,15 @@ language_tai = { tfe = { language_tai } vanilla = { language_tai } } + + MOD_DEPENDENT = { + IF wtwsms = { + parameters = { + language_branch_tai = yes + language_family_kra_dai = yes + } + } + } type = language is_shown = { @@ -608,11 +1135,33 @@ language_tai = { LANGUAGE = language_tai } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_tai } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_tai } + multiply = 10 + } + else_if = { + limit = { has_cultural_parameter = language_branch_tai } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_kra_dai } + multiply = same_language_family_choice_factor + } + } + } + ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_tai } + multiply = 10 + } + } } } @@ -624,6 +1173,11 @@ language_palaungic = { # from AsiaExtended mod https://steamcommunity.com/shared tfe = { language_palaungic } vanilla = { language_palaungic } } + + parameters = { + language_branch_palaungic = yes + language_family_austro_asiatic = yes # already exists in WtWSMS + } type = language is_shown = { @@ -631,11 +1185,32 @@ language_palaungic = { # from AsiaExtended mod https://steamcommunity.com/shared LANGUAGE = language_palaungic } } - ai_will_do = { - value = 10 - if = { - limit = { has_cultural_pillar = language_palaungic } - multiply = 10 + + MOD_DEPENDENT = { + IF wtwsms = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_palaungic } + multiply = same_language_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_branch_palaungic } + multiply = same_language_branch_choice_factor + } + else_if = { + limit = { has_cultural_parameter = language_family_austro_asiatic } + multiply = same_language_family_choice_factor + } + } + } ELSE = { + ai_will_do = { + value = 10 + if = { + limit = { has_cultural_pillar = language_palaungic } + multiply = 10 + } + } } } diff --git a/ImperatorToCK3/Data_Files/configurables/localization/english/ccu_language_parameters_l_english.yml b/ImperatorToCK3/Data_Files/configurables/localization/english/ccu_language_parameters_l_english.yml new file mode 100644 index 000000000..ab89bfca2 --- /dev/null +++ b/ImperatorToCK3/Data_Files/configurables/localization/english/ccu_language_parameters_l_english.yml @@ -0,0 +1,13 @@ +l_english: + # language families + culture_parameter_language_family_kra_dai: "#P +[EmptyScope.ScriptValue('same_language_family_cultural_acceptance')|0]#! [cultural_acceptance_baseline|E] with Cultures sharing the Kra–Dai [language_family|E]" + culture_parameter_language_family_elamite_family: "#P +[EmptyScope.ScriptValue('same_language_family_cultural_acceptance')|0]#! [cultural_acceptance_baseline|E] with Cultures sharing the Elamite [language_family|E]" + culture_parameter_language_family_paleo_sardinian: "#P +[EmptyScope.ScriptValue('same_language_family_cultural_acceptance')|0]#! [cultural_acceptance_baseline|E] with Cultures sharing the Paleo-Sardinian [language_family|E]" + culture_parameter_language_family_tyrsenian: "#P +[EmptyScope.ScriptValue('same_language_family_cultural_acceptance')|0]#! [cultural_acceptance_baseline|E] with Cultures sharing the Tyrsenian [language_family|E]" + + # language branches + culture_parameter_language_branch_palaungic: "#P +[EmptyScope.ScriptValue('same_language_branch_cultural_acceptance')|0]#! [cultural_acceptance_baseline|E] with Cultures sharing the Palaungic [language_branch|E]" + culture_parameter_language_branch_tai: "#P +[EmptyScope.ScriptValue('same_language_branch_cultural_acceptance')|0]#! [cultural_acceptance_baseline|E] with Cultures sharing the Tai [language_branch|E]" + culture_parameter_language_branch_nuragic: "#P +[EmptyScope.ScriptValue('same_language_branch_cultural_acceptance')|0]#! [cultural_acceptance_baseline|E] with Cultures sharing the Nuragic [language_branch|E]" + culture_parameter_language_branch_elamite_group: "#P +[EmptyScope.ScriptValue('same_language_branch_cultural_acceptance')|0]#! [cultural_acceptance_baseline|E] with Cultures sharing the Elamite [language_branch|E]" + culture_parameter_language_branch_etruscan: "#P +[EmptyScope.ScriptValue('same_language_branch_cultural_acceptance')|0]#! [cultural_acceptance_baseline|E] with Cultures sharing the Etruscan [language_branch|E]" \ No newline at end of file diff --git a/ImperatorToCK3/Data_Files/converter_globals/FAQ.txt b/ImperatorToCK3/Data_Files/converter_globals/FAQ.txt index e34f37803..181d4512d 100644 --- a/ImperatorToCK3/Data_Files/converter_globals/FAQ.txt +++ b/ImperatorToCK3/Data_Files/converter_globals/FAQ.txt @@ -25,6 +25,7 @@ A: The converter officially supports these Imperator mods: - 2.0 Better UI (https://steamcommunity.com/sharedfiles/filedetails/?id=1845165876) The converter also supports these CK3 mods: - The Fallen Eagle (https://steamcommunity.com/sharedfiles/filedetails/?id=2243307127) +- When the World Stopped Making Sense (https://steamcommunity.com/sharedfiles/filedetails/?id=2858562094) As for other mods, unless they change the map or how cultures, religions or flags work, you can probably use them. Total map overhauls are not supported (of course), and whatever new cultures and religions are brought by the mod - you'll have to add manually in the files in configurables folder. diff --git a/ImperatorToCK3/ImperatorToCK3.csproj b/ImperatorToCK3/ImperatorToCK3.csproj index a5bd45c5e..5dfb688a0 100644 --- a/ImperatorToCK3/ImperatorToCK3.csproj +++ b/ImperatorToCK3/ImperatorToCK3.csproj @@ -34,6 +34,8 @@ + + all diff --git a/ImperatorToCK3/Outputter/CulturesOutputter.cs b/ImperatorToCK3/Outputter/CulturesOutputter.cs index da8200669..de638163f 100644 --- a/ImperatorToCK3/Outputter/CulturesOutputter.cs +++ b/ImperatorToCK3/Outputter/CulturesOutputter.cs @@ -1,15 +1,24 @@ using commonItems; +using commonItems.Mods; using commonItems.Serialization; +using CWTools.CSharp; +using CWTools.Parser; +using CWTools.Process; +using ImperatorToCK3.CK3; using ImperatorToCK3.CK3.Cultures; using ImperatorToCK3.CommonUtils; +using Microsoft.FSharp.Collections; +using System.Collections; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Threading.Tasks; namespace ImperatorToCK3.Outputter; public static class CulturesOutputter { - public static async Task OutputCultures(string outputModPath, CultureCollection cultures, Date date) { + public static async Task OutputCultures(string outputModPath, CultureCollection cultures, ModFilesystem ck3ModFS, Configuration config, Date date) { Logger.Info("Outputting cultures..."); var sb = new StringBuilder(); @@ -22,6 +31,10 @@ public static async Task OutputCultures(string outputModPath, CultureCollection await output.WriteAsync(sb.ToString()); await OutputCultureHistory(outputModPath, cultures, date); + + if (config.WhenTheWorldStoppedMakingSenseEnabled) { + OutputCCULanguageParameters(outputModPath, ck3ModFS, config.GetCK3ModFlags()); + } } private static async Task OutputCultureHistory(string outputModPath, CultureCollection cultures, Date date) { @@ -31,4 +44,148 @@ private static async Task OutputCultureHistory(string outputModPath, CultureColl await culture.OutputHistory(outputModPath, date); } } + + private static void OutputCCULanguageParameters(string outputModPath, ModFilesystem ck3ModFS, IDictionary ck3ModFlags) { + Logger.Info("Outputting CCU language parameters for WtWSMS..."); + List languageFamilyParameters = []; + List languageBranchParameters = []; + + // Read converter-added language families and branches from the configurable. + var fileParser = new Parser(); + fileParser.RegisterKeyword("language_families", reader => { + var familiesParser = new Parser(); + familiesParser.RegisterModDependentBloc(ck3ModFlags); + familiesParser.RegisterRegex(CommonRegexes.Catchall, (_, familyParameter) => { + languageFamilyParameters.Add(familyParameter); + }); + familiesParser.ParseStream(reader); + }); + fileParser.RegisterKeyword("language_branches", reader => { + var branchesParser = new Parser(); + branchesParser.RegisterModDependentBloc(ck3ModFlags); + branchesParser.RegisterRegex(CommonRegexes.Catchall, (_, branchParameter) => { + languageBranchParameters.Add(branchParameter); + }); + branchesParser.ParseStream(reader); + }); + fileParser.ParseFile("configurables/ccu_language_parameters.txt"); + + // Modify the common\scripted_effects\ccu_scripted_effects.txt file. + var relativePath = "common/scripted_effects/ccu_scripted_effects.txt"; + // Modify the common\scripted_effects\ccu_scripted_effects.txt file. + var scriptedEffectsPath = ck3ModFS.GetActualFileLocation(relativePath); + if (scriptedEffectsPath is null) { + Logger.Warn("Could not find ccu_scripted_effects.txt in the CK3 mod. Aborting the outputting of language parameters."); + } + + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + var fileName = Path.GetFileName(scriptedEffectsPath); + var statements = CKParser.parseFile(scriptedEffectsPath).GetResult(); + var rootNode = Parsers.ProcessStatements(fileName, scriptedEffectsPath, statements); + var nodes = rootNode.Nodes.ToArray(); + + var familyEffectNode = nodes.FirstOrDefault(n => n.Key == "ccu_initialize_language_family_effect"); + if (familyEffectNode is null) { + Logger.Warn("ccu_initialize_language_family_effect effect not found!"); + return; + } + List allChildren = familyEffectNode.AllChildren; + foreach (var languageFamily in languageFamilyParameters) { + var statementsForFamily = CKParser.parseString( + $$""" + else_if = { + limit = { has_cultural_parameter = {{languageFamily}} } + set_variable = { name = language_family value = flag:{{languageFamily}} } + } + """, fileName).GetResult(); + + var rootNodeForFamily = Parsers.ProcessStatements(fileName, scriptedEffectsPath, statementsForFamily); + allChildren.Add(Child.NewNodeC(rootNodeForFamily.Nodes.First())); + } + familyEffectNode.AllChildren = allChildren; + + var branchEffectNode = nodes.FirstOrDefault(n => n.Key == "ccu_initialize_language_branch_effect"); + if (branchEffectNode is null) { + Logger.Warn("ccu_initialize_language_branch_effect effect not found!"); + return; + } + allChildren = branchEffectNode.AllChildren; + foreach (var languageBranch in languageBranchParameters) { + var statementsForBranch = CKParser.parseString( + $$""" + else_if = { + limit = { has_cultural_parameter = {{languageBranch}} } + set_variable = { name = language_branch value = flag:{{languageBranch}} } + } + """, fileName).GetResult(); + + var rootNodeForBranch = Parsers.ProcessStatements(fileName, scriptedEffectsPath, statementsForBranch); + allChildren.Add(Child.NewNodeC(rootNodeForBranch.Nodes.First())); + } + branchEffectNode.AllChildren = allChildren; + + // Output the modified file. + var tooutput = rootNode.AllChildren + .Select(c => { + if (c.IsLeafC) { + return c.leaf.ToRaw; + } else if (c.IsNodeC) { + return c.node.ToRaw; + } + + return null; + }) + .Where(s => s is not null) + .Cast() + .ToList(); + var fsharpList = ListModule.OfSeq(tooutput); + + var outputFilePath = Path.Join(outputModPath, relativePath); + // Output the file with UTF8-BOM encoding. + File.WriteAllText(outputFilePath, CKPrinter.printTopLevelKeyValueList(fsharpList), Encoding.UTF8); + + // Add the language parameters to common/scripted_guis/ccu_error_suppression.txt. + // This is what WtWSMS does for the parameters it adds. + var errorSuppressionRelativePath = "common/scripted_guis/ccu_error_suppression.txt"; + var errorSuppressionPath = ck3ModFS.GetActualFileLocation(errorSuppressionRelativePath); + if (errorSuppressionPath is null) { + Logger.Warn("Could not find ccu_error_suppression.txt in the CK3 mod. " + + "Some harmless errors related to converter-added language parameters may appear in error.log."); + return; + } + + // In the file, add the last line that contains: "if = { limit = { var:temp = flag:language_family", + // and add a new line "TEMPORARY LINE" after it. + // Do the same for language branches. + var errorSuppressionContent = File.ReadAllText(errorSuppressionPath); + var contentLines = errorSuppressionContent.Split('\n'); + var newContent = new StringBuilder(); + foreach (var line in contentLines) { + newContent.AppendLine(line.TrimEnd()); + if (line.Contains("if = { limit = { var:temp = flag:language_family_uralic }")) { + foreach (var familyParameter in languageFamilyParameters) { + newContent.AppendLine( + $$""" + if = { + limit = { var:temp = flag:{{familyParameter}} } + set_variable = { name = temp value = flag:{{familyParameter}} } + } + """); + } + } else if (line.Contains("if = { limit = { var:temp = flag:language_branch_yeniseian }")) { + foreach (var branchParameter in languageBranchParameters) { + newContent.AppendLine( + $$""" + if = { + limit = { var:temp = flag:{{branchParameter}} } + set_variable = { name = temp value = flag:{{branchParameter}} } + } + """); + } + } + } + outputFilePath = Path.Join(outputModPath, errorSuppressionRelativePath); + File.WriteAllText(outputFilePath, newContent.ToString(), Encoding.UTF8); + } } \ No newline at end of file diff --git a/ImperatorToCK3/Outputter/OnActionOutputter.cs b/ImperatorToCK3/Outputter/OnActionOutputter.cs index 111644b80..246464468 100644 --- a/ImperatorToCK3/Outputter/OnActionOutputter.cs +++ b/ImperatorToCK3/Outputter/OnActionOutputter.cs @@ -16,7 +16,7 @@ public static async Task OutputEverything(Configuration config, ModFilesystem ck if (config.FallenEagleEnabled) { await DisableUnneededFallenEagleOnActions(outputModPath); await RemoveStruggleStartFromFallenEagleOnActions(ck3ModFS, outputModPath); - } else { // vanilla + } else if (!config.WhenTheWorldStoppedMakingSenseEnabled) { // vanilla await RemoveUnneededPartsOfVanillaOnActions(ck3ModFS, outputModPath); } Logger.IncrementProgress(); diff --git a/ImperatorToCK3/Outputter/WorldOutputter.cs b/ImperatorToCK3/Outputter/WorldOutputter.cs index b38f8e2e3..f5b559fa3 100644 --- a/ImperatorToCK3/Outputter/WorldOutputter.cs +++ b/ImperatorToCK3/Outputter/WorldOutputter.cs @@ -35,7 +35,7 @@ public static void OutputWorld(World ck3World, Imperator.World imperatorWorld, C TitlesOutputter.OutputTitles(outputPath, ck3World.LandedTitles), PillarOutputter.OutputPillars(outputPath, ck3World.CulturalPillars), - CulturesOutputter.OutputCultures(outputPath, ck3World.Cultures, ck3World.CorrectedDate), + CulturesOutputter.OutputCultures(outputPath, ck3World.Cultures, ck3World.ModFS, config, ck3World.CorrectedDate), ReligionsOutputter.OutputReligionsAndHolySites(outputPath, ck3World.Religions, ck3World.LocDB), @@ -190,6 +190,8 @@ private static void CreateFolders(string outputPath) { SystemUtils.TryCreateFolder(Path.Combine(outputPath, "common", "on_action")); SystemUtils.TryCreateFolder(Path.Combine(outputPath, "common", "religion", "holy_sites")); SystemUtils.TryCreateFolder(Path.Combine(outputPath, "common", "religion", "religions")); + SystemUtils.TryCreateFolder(Path.Combine(outputPath, "common", "scripted_effects")); + SystemUtils.TryCreateFolder(Path.Combine(outputPath, "common", "scripted_guis")); SystemUtils.TryCreateFolder(Path.Combine(outputPath, "common", "scripted_triggers")); SystemUtils.TryCreateFolder(Path.Combine(outputPath, "events")); SystemUtils.TryCreateFolder(Path.Combine(outputPath, "gui"));