Skip to content

Commit ac41843

Browse files
authored
Merge branch 'master' into feature/rajas-of-asia-support
2 parents 5590e9e + 4e7f038 commit ac41843

File tree

9 files changed

+111
-43
lines changed

9 files changed

+111
-43
lines changed

.github/workflows/build.yml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ jobs:
1313
strategy:
1414
matrix:
1515
os: [[self-hosted, windows], [self-hosted, linux], macos-14]
16+
fail-fast: false
1617
runs-on: ${{ matrix.os }}
1718
steps:
1819
- uses: actions/checkout@v4

ImperatorToCK3.UnitTests/Mappers/TagTitle/TagTitleMapperTests.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -191,16 +191,16 @@ public void TitleCanBeGeneratedFromGovernorship() {
191191
}
192192

193193
[Fact]
194-
public void GetTitleForTagReturnsNullOnEmptyTag() {
194+
public void GetTitleForTagUsesCountryIdIfTagIsEmpty() {
195195
var mapper = new TagTitleMapper(tagTitleMappingsPath, governorshipTitleMappingsPath, rankMappingsPath);
196196
var country = Country.Parse(new BufferedReader(string.Empty), 1);
197197
Assert.Empty(country.Tag);
198198
var match = mapper.GetTitleForTag(country, "", maxTitleRank: TitleRank.empire);
199199

200-
Assert.Null(match);
200+
Assert.Equal("d_IRTOCK3_id_1", match);
201201
}
202202
[Fact]
203-
public void GetTitleGovernorshipTagReturnsNullOnCountryWithNoCK3Title() {
203+
public void GetTitleForGovernorshipReturnsNullOnCountryWithNoCK3Title() {
204204
var output = new StringWriter();
205205
Console.SetOut(output);
206206

ImperatorToCK3.UnitTests/Mappers/TagTitle/TitleMappingTests.cs

+44-6
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ public class TitleMappingTests {
2525
public void SimpleTagMatch() {
2626
var reader = new BufferedReader("{ ck3 = e_roman_empire ir = ROM }");
2727
var mapping = TitleMapping.Parse(reader);
28-
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);
28+
29+
var country = new Country(1) {Tag = "ROM"};
30+
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);
2931

3032
Assert.Equal("e_roman_empire", match);
3133
}
@@ -34,7 +36,9 @@ public void SimpleTagMatch() {
3436
public void SimpleTagMatchFailsOnWrongTag() {
3537
var reader = new BufferedReader("{ ck3 = e_roman_empire ir = REM }");
3638
var mapping = TitleMapping.Parse(reader);
37-
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);
39+
40+
var country = new Country(1) {Tag = "ROM"};
41+
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);
3842

3943
Assert.Null(match);
4044
}
@@ -43,7 +47,9 @@ public void SimpleTagMatchFailsOnWrongTag() {
4347
public void SimpleTagMatchFailsOnNoTag() {
4448
var reader = new BufferedReader("{ ck3 = e_roman_empire }");
4549
var mapping = TitleMapping.Parse(reader);
46-
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);
50+
51+
var country = new Country(1) {Tag = "ROM"};
52+
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);
4753

4854
Assert.Null(match);
4955
}
@@ -52,7 +58,9 @@ public void SimpleTagMatchFailsOnNoTag() {
5258
public void TagRankMatch() {
5359
var reader = new BufferedReader("{ ck3 = e_roman_empire ir = ROM rank = e }");
5460
var mapping = TitleMapping.Parse(reader);
55-
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);
61+
62+
var country = new Country(1) {Tag = "ROM"};
63+
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);
5664

5765
Assert.Equal("e_roman_empire", match);
5866
}
@@ -61,7 +69,9 @@ public void TagRankMatch() {
6169
public void TagRankMatchFailsOnWrongRank() {
6270
var reader = new BufferedReader("{ ck3 = e_roman_empire ir = ROM rank = k }");
6371
var mapping = TitleMapping.Parse(reader);
64-
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);
72+
73+
var country = new Country(1) {Tag = "ROM"};
74+
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);
6575

6676
Assert.Null(match);
6777
}
@@ -70,11 +80,39 @@ public void TagRankMatchFailsOnWrongRank() {
7080
public void TagRankMatchSucceedsOnNoRank() {
7181
var reader = new BufferedReader("{ ck3 = e_roman_empire ir = ROM }");
7282
var mapping = TitleMapping.Parse(reader);
73-
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);
83+
84+
var country = new Country(1) {Tag = "ROM"};
85+
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);
7486

7587
Assert.Equal("e_roman_empire", match);
7688
}
7789

90+
[Fact]
91+
public void CountryNameCanBeUsedInAMapping() {
92+
var reader = new BufferedReader("""
93+
{
94+
ck3 = e_byzantium
95+
ir_name_key = ERE
96+
ir_name_key = eastern_roman_republic_name # multiple ir_name_keys are allowed
97+
}
98+
""");
99+
var mapping = TitleMapping.Parse(reader);
100+
101+
// If the mapping has no "ir" parameter, and the country name key matches the ones in the mapping,
102+
// the mapping should match regardless of the tag.
103+
var ereCountry1 = new Country(1) {Tag = "X01", CountryName = new() { Name = "ERE" }};
104+
var ereCountry2 = new Country(2) {Tag = "X02", CountryName = new() { Name = "eastern_roman_republic_name" }};
105+
Assert.Equal("e_byzantium", mapping.RankMatch(ereCountry1, TitleRank.empire, maxTitleRank: TitleRank.empire));
106+
Assert.Equal("e_byzantium", mapping.RankMatch(ereCountry2, TitleRank.empire, maxTitleRank: TitleRank.empire));
107+
108+
var nonEreCountry = new Country(3) {Tag = "X03", CountryName = new() { Name = "non_ERE" }};
109+
Assert.Null(mapping.RankMatch(nonEreCountry, TitleRank.empire, maxTitleRank: TitleRank.empire));
110+
111+
// A country with tag ERE should not match, we're comparing names!
112+
var ereTagCountry = new Country(4) {Tag = "ERE", CountryName = new() { Name = "non_ERE" }};
113+
Assert.Null(mapping.RankMatch(ereTagCountry, TitleRank.empire, maxTitleRank: TitleRank.empire));
114+
}
115+
78116
[Fact]
79117
public void GovernorshipToDeJureDuchyMappingFailsIfDuchyIsNot60PercentControlled() {
80118
var irProvinces = new ProvinceCollection {

ImperatorToCK3/Data_Files/configurables/title_map.txt

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
### I:R - CK3 tag mappings ###
2-
#link = { ir = a ck3 = b rank = c }
2+
#link = { ir = a ck3 = b rank = c ir_name_key = d }
33
#a = the I:R tag
44
#b = the CK3 landed title
55
#c = the rank this country has (can only be "d", "k", "e")
6+
#d = (optional) the localization key the I:R country uses, for example "eastern_roman_republic_name". Multiple entries are allowed.
67

78
# Only countries with a "Kingdom" or "Empire" in name should be
89
# let map into a title with a different rank than the output
@@ -47,6 +48,15 @@ link = { ir = JUD ck3 = k_israel rank = k }
4748

4849
# Italic
4950

51+
link = {
52+
ck3 = e_byzantium
53+
ir_name_key = ERE
54+
ir_name_key = eastern_rome_name
55+
ir_name_key = eastern_roman_republic_name
56+
ir_name_key = eastern_roman_empire_name
57+
ir_name_key = eastern_roman_kingdom_name
58+
ir_name_key = eastern_roman_dictatorship_name
59+
}
5060
link = { ir = ROM ck3 = e_roman_empire rank = e }
5161
link = { ir = ROM ck3 = e_roman_empire rank = k } # Because yes
5262
link = { ir = NEP ck3 = k_naples rank = k }

ImperatorToCK3/Imperator/Countries/Country.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ internal sealed partial class Country : IIdentifiable<ulong> {
1818
public string? Religion { get; private set; }
1919
public List<RulerTerm> RulerTerms { get; set; } = [];
2020
public Dictionary<string, int> HistoricalRegnalNumbers { get; private set; } = [];
21-
public string Tag { get; private set; } = "";
21+
22+
private string tag = "";
23+
public string Tag {
24+
get => tag;
25+
init => tag = value;
26+
}
27+
2228
private string? historicalTag;
2329
public string HistoricalTag {
2430
get => historicalTag ?? Tag;
@@ -29,7 +35,13 @@ public string HistoricalTag {
2935
public Country? OriginCountry { get; private set; } = null;
3036

3137
public string Name => CountryName.Name;
32-
public CountryName CountryName { get; private set; } = new();
38+
39+
private CountryName countryName = new();
40+
public CountryName CountryName {
41+
get => countryName;
42+
init => countryName = value;
43+
}
44+
3345
public string Flag { get; private set; } = "";
3446
public CountryType CountryType { get; private set; } = CountryType.real;
3547
public ulong? CapitalProvinceId { get; private set; }

ImperatorToCK3/Imperator/Countries/CountryFactory.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ internal sealed partial class Country {
2424
private static void RegisterCountryKeywords(Parser parser, Country parsedCountry) {
2525
var colorFactory = new ColorFactory();
2626

27-
parser.RegisterKeyword("tag", reader => parsedCountry.Tag = reader.GetString());
27+
parser.RegisterKeyword("tag", reader => parsedCountry.tag = reader.GetString());
2828
parser.RegisterKeyword("historical", reader => parsedCountry.HistoricalTag = reader.GetString());
2929
parser.RegisterKeyword("origin", reader => parsedCountry.parsedOriginCountryId = reader.GetULong());
30-
parser.RegisterKeyword("country_name", reader => parsedCountry.CountryName = CountryName.Parse(reader));
30+
parser.RegisterKeyword("country_name", reader => parsedCountry.countryName = CountryName.Parse(reader));
3131
parser.RegisterKeyword("flag", reader => parsedCountry.Flag = reader.GetString());
3232

3333
parser.RegisterKeyword("country_type", reader => SetCountryType(reader, parsedCountry));

ImperatorToCK3/Imperator/Countries/CountryName.cs

+12-15
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
using commonItems;
22
using commonItems.Localization;
3-
using System;
43
using System.Linq;
54

65
namespace ImperatorToCK3.Imperator.Countries;
76

8-
internal sealed class CountryName : ICloneable {
9-
public string Name { get; private set; } = "";
7+
internal sealed class CountryName {
8+
public string Name { get; init; } = "";
109
private string? adjective;
11-
public CountryName? BaseName { get; private set; }
10+
public CountryName? BaseName { get; private init; }
1211

13-
public object Clone() {
14-
return new CountryName {Name = Name, adjective = adjective, BaseName = BaseName};
15-
}
1612

1713
public LocBlock? GetNameLocBlock(LocDB irLocDB, CountryCollection imperatorCountries) {
1814
// If the name contains a space, it can be a composite name like "egyptian PROV4791_persia"
@@ -190,18 +186,19 @@ private string ReplaceDataTypes(string loc, string language, LocDB irLocDB, Coun
190186
}
191187

192188
public static CountryName Parse(BufferedReader reader) {
193-
var countryName = new CountryName();
194-
189+
string parsedName = string.Empty;
190+
string? parsedAdjective = null;
191+
CountryName? parsedBaseName = null;
192+
195193
var parser = new Parser();
196-
parser.RegisterKeyword("name", r => countryName.Name = r.GetString());
197-
parser.RegisterKeyword("adjective", r => countryName.adjective = r.GetString());
194+
parser.RegisterKeyword("name", r => parsedName = r.GetString());
195+
parser.RegisterKeyword("adjective", r => parsedAdjective = r.GetString());
198196
parser.RegisterKeyword("base", r => {
199-
var tempCountryName = (CountryName)countryName.Clone();
200-
tempCountryName.BaseName = Parse(r);
201-
countryName = (CountryName)tempCountryName.Clone();
197+
parsedBaseName = Parse(r);
202198
});
203199
parser.IgnoreAndLogUnregisteredItems();
204200
parser.ParseStream(reader);
205-
return countryName;
201+
202+
return new() {Name = parsedName, adjective = parsedAdjective, BaseName = parsedBaseName};
206203
}
207204
}

ImperatorToCK3/Mappers/TagTitle/TagTitleMapper.cs

+7-10
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,6 @@ public void RegisterGovernorship(string imperatorRegion, string imperatorCountry
3636
usedTitles.Add(ck3Title);
3737
}
3838
public string? GetTitleForTag(Country country, string localizedTitleName, TitleRank maxTitleRank) {
39-
// If country has an origin (e.g. rebelled from another country), the historical tag probably points to the original country.
40-
string tagForMapping = country.OriginCountry is not null ? country.Tag : country.HistoricalTag;
41-
42-
// The only case where we fail is on invalid invocation. Otherwise, failure is not an option!
43-
if (string.IsNullOrEmpty(tagForMapping)) {
44-
return null;
45-
}
46-
4739
// Look up register.
4840
if (registeredCountryTitles.TryGetValue(country.Id, out var titleToReturn)) {
4941
return titleToReturn;
@@ -52,7 +44,7 @@ public void RegisterGovernorship(string imperatorRegion, string imperatorCountry
5244
// Attempt a title match.
5345
var rank = EnumHelper.Min(GetCK3TitleRank(country, localizedTitleName), maxTitleRank);
5446
foreach (var mapping in titleMappings) {
55-
var match = mapping.RankMatch(tagForMapping, rank, maxTitleRank);
47+
var match = mapping.RankMatch(country, rank, maxTitleRank);
5648
if (match is not null) {
5749
if (usedTitles.Contains(match)) {
5850
continue;
@@ -244,7 +236,12 @@ private string GenerateNewTitleId(Country country, string localizedTitleName, Ti
244236

245237
var ck3TitleId = GetTitlePrefixForRank(ck3Rank);
246238
ck3TitleId += GeneratedCK3TitlePrefix;
247-
ck3TitleId += country.Tag;
239+
240+
if (string.IsNullOrEmpty(country.Tag)) {
241+
ck3TitleId += $"id_{country.Id}";
242+
} else {
243+
ck3TitleId += country.Tag;
244+
}
248245

249246
return ck3TitleId;
250247
}

ImperatorToCK3/Mappers/TagTitle/TitleMapping.cs

+17-4
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
using commonItems;
22
using ImperatorToCK3.CK3.Titles;
3+
using ImperatorToCK3.Imperator.Countries;
34
using ImperatorToCK3.Imperator.Jobs;
45
using ImperatorToCK3.Imperator.Provinces;
56
using ImperatorToCK3.Mappers.Province;
67
using Open.Collections;
7-
using System;
88
using System.Collections.Generic;
99
using System.Collections.Immutable;
1010
using System.Linq;
1111

1212
namespace ImperatorToCK3.Mappers.TagTitle;
1313

1414
internal sealed class TitleMapping {
15-
public string? RankMatch(string irTagOrRegion, TitleRank rank, TitleRank maxTitleRank) {
16-
if (imperatorTagOrRegion != irTagOrRegion) {
15+
public string? RankMatch(Country country, TitleRank rank, TitleRank maxTitleRank) {
16+
// If country has an origin (e.g. rebelled from another country), the historical tag probably points to the original country.
17+
string tagForMapping = country.OriginCountry is not null ? country.Tag : country.HistoricalTag;
18+
19+
// The mapping should have at least an I:R tag or I:R name keys.
20+
if (imperatorTagOrRegion is null & irNameKeys.Count == 0) {
21+
return null;
22+
}
23+
24+
if (imperatorTagOrRegion is not null & imperatorTagOrRegion != tagForMapping) {
1725
return null;
1826
}
1927
if (maxTitleRank < CK3TitleRank) {
@@ -22,6 +30,9 @@ internal sealed class TitleMapping {
2230
if (ranks.Count > 0 && !ranks.Contains(rank)) {
2331
return null;
2432
}
33+
if (irNameKeys.Count > 0 && !irNameKeys.Contains(country.CountryName.Name)) {
34+
return null;
35+
}
2536
return ck3TitleId;
2637
}
2738

@@ -60,8 +71,9 @@ internal sealed class TitleMapping {
6071
}
6172

6273
private string ck3TitleId = string.Empty;
63-
private string imperatorTagOrRegion = string.Empty;
74+
private string? imperatorTagOrRegion;
6475
private readonly SortedSet<TitleRank> ranks = [];
76+
private readonly SortedSet<string> irNameKeys = [];
6577

6678
private TitleRank CK3TitleRank => Title.GetRankForId(ck3TitleId);
6779

@@ -74,6 +86,7 @@ static TitleMapping() {
7486
var ranksToAdd = reader.GetString().ToCharArray().Select(TitleRankUtils.CharToTitleRank);
7587
mappingToReturn.ranks.AddRange(ranksToAdd);
7688
});
89+
parser.RegisterKeyword("ir_name_key", reader => mappingToReturn.irNameKeys.Add(reader.GetString()));
7790
parser.IgnoreAndLogUnregisteredItems();
7891
}
7992
public static TitleMapping Parse(BufferedReader reader) {

0 commit comments

Comments
 (0)