Skip to content

Commit

Permalink
Add a name-based country mapping to e_byzantium (#2467)
Browse files Browse the repository at this point in the history
  • Loading branch information
IhateTrains authored Jan 22, 2025
1 parent 2515e0a commit 4e7f038
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 43 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
strategy:
matrix:
os: [[self-hosted, windows], [self-hosted, linux], macos-14]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,16 +191,16 @@ public void TitleCanBeGeneratedFromGovernorship() {
}

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

Assert.Null(match);
Assert.Equal("d_IRTOCK3_id_1", match);
}
[Fact]
public void GetTitleGovernorshipTagReturnsNullOnCountryWithNoCK3Title() {
public void GetTitleForGovernorshipReturnsNullOnCountryWithNoCK3Title() {
var output = new StringWriter();
Console.SetOut(output);

Expand Down
50 changes: 44 additions & 6 deletions ImperatorToCK3.UnitTests/Mappers/TagTitle/TitleMappingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ public class TitleMappingTests {
public void SimpleTagMatch() {
var reader = new BufferedReader("{ ck3 = e_roman_empire ir = ROM }");
var mapping = TitleMapping.Parse(reader);
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);

var country = new Country(1) {Tag = "ROM"};
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);

Assert.Equal("e_roman_empire", match);
}
Expand All @@ -34,7 +36,9 @@ public void SimpleTagMatch() {
public void SimpleTagMatchFailsOnWrongTag() {
var reader = new BufferedReader("{ ck3 = e_roman_empire ir = REM }");
var mapping = TitleMapping.Parse(reader);
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);

var country = new Country(1) {Tag = "ROM"};
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);

Assert.Null(match);
}
Expand All @@ -43,7 +47,9 @@ public void SimpleTagMatchFailsOnWrongTag() {
public void SimpleTagMatchFailsOnNoTag() {
var reader = new BufferedReader("{ ck3 = e_roman_empire }");
var mapping = TitleMapping.Parse(reader);
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);

var country = new Country(1) {Tag = "ROM"};
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);

Assert.Null(match);
}
Expand All @@ -52,7 +58,9 @@ public void SimpleTagMatchFailsOnNoTag() {
public void TagRankMatch() {
var reader = new BufferedReader("{ ck3 = e_roman_empire ir = ROM rank = e }");
var mapping = TitleMapping.Parse(reader);
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);

var country = new Country(1) {Tag = "ROM"};
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);

Assert.Equal("e_roman_empire", match);
}
Expand All @@ -61,7 +69,9 @@ public void TagRankMatch() {
public void TagRankMatchFailsOnWrongRank() {
var reader = new BufferedReader("{ ck3 = e_roman_empire ir = ROM rank = k }");
var mapping = TitleMapping.Parse(reader);
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);

var country = new Country(1) {Tag = "ROM"};
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);

Assert.Null(match);
}
Expand All @@ -70,11 +80,39 @@ public void TagRankMatchFailsOnWrongRank() {
public void TagRankMatchSucceedsOnNoRank() {
var reader = new BufferedReader("{ ck3 = e_roman_empire ir = ROM }");
var mapping = TitleMapping.Parse(reader);
var match = mapping.RankMatch("ROM", TitleRank.empire, maxTitleRank: TitleRank.empire);

var country = new Country(1) {Tag = "ROM"};
var match = mapping.RankMatch(country, TitleRank.empire, maxTitleRank: TitleRank.empire);

Assert.Equal("e_roman_empire", match);
}

[Fact]
public void CountryNameCanBeUsedInAMapping() {
var reader = new BufferedReader("""
{
ck3 = e_byzantium
ir_name_key = ERE
ir_name_key = eastern_roman_republic_name # multiple ir_name_keys are allowed
}
""");
var mapping = TitleMapping.Parse(reader);

// If the mapping has no "ir" parameter, and the country name key matches the ones in the mapping,
// the mapping should match regardless of the tag.
var ereCountry1 = new Country(1) {Tag = "X01", CountryName = new() { Name = "ERE" }};
var ereCountry2 = new Country(2) {Tag = "X02", CountryName = new() { Name = "eastern_roman_republic_name" }};
Assert.Equal("e_byzantium", mapping.RankMatch(ereCountry1, TitleRank.empire, maxTitleRank: TitleRank.empire));
Assert.Equal("e_byzantium", mapping.RankMatch(ereCountry2, TitleRank.empire, maxTitleRank: TitleRank.empire));

var nonEreCountry = new Country(3) {Tag = "X03", CountryName = new() { Name = "non_ERE" }};
Assert.Null(mapping.RankMatch(nonEreCountry, TitleRank.empire, maxTitleRank: TitleRank.empire));

// A country with tag ERE should not match, we're comparing names!
var ereTagCountry = new Country(4) {Tag = "ERE", CountryName = new() { Name = "non_ERE" }};
Assert.Null(mapping.RankMatch(ereTagCountry, TitleRank.empire, maxTitleRank: TitleRank.empire));
}

[Fact]
public void GovernorshipToDeJureDuchyMappingFailsIfDuchyIsNot60PercentControlled() {
var irProvinces = new ProvinceCollection {
Expand Down
12 changes: 11 additions & 1 deletion ImperatorToCK3/Data_Files/configurables/title_map.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
### I:R - CK3 tag mappings ###
#link = { ir = a ck3 = b rank = c }
#link = { ir = a ck3 = b rank = c ir_name_key = d }
#a = the I:R tag
#b = the CK3 landed title
#c = the rank this country has (can only be "d", "k", "e")
#d = (optional) the localization key the I:R country uses, for example "eastern_roman_republic_name". Multiple entries are allowed.

# Only countries with a "Kingdom" or "Empire" in name should be
# let map into a title with a different rank than the output
Expand Down Expand Up @@ -41,6 +42,15 @@ link = { ir = JUD ck3 = k_israel rank = k }

# Italic

link = {
ck3 = e_byzantium
ir_name_key = ERE
ir_name_key = eastern_rome_name
ir_name_key = eastern_roman_republic_name
ir_name_key = eastern_roman_empire_name
ir_name_key = eastern_roman_kingdom_name
ir_name_key = eastern_roman_dictatorship_name
}
link = { ir = ROM ck3 = e_roman_empire rank = e }
link = { ir = ROM ck3 = e_roman_empire rank = k } # Because yes
link = { ir = NEP ck3 = k_naples rank = k }
Expand Down
16 changes: 14 additions & 2 deletions ImperatorToCK3/Imperator/Countries/Country.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ internal sealed partial class Country : IIdentifiable<ulong> {
public string? Religion { get; private set; }
public List<RulerTerm> RulerTerms { get; set; } = [];
public Dictionary<string, int> HistoricalRegnalNumbers { get; private set; } = [];
public string Tag { get; private set; } = "";

private string tag = "";
public string Tag {
get => tag;
init => tag = value;
}

private string? historicalTag;
public string HistoricalTag {
get => historicalTag ?? Tag;
Expand All @@ -29,7 +35,13 @@ public string HistoricalTag {
public Country? OriginCountry { get; private set; } = null;

public string Name => CountryName.Name;
public CountryName CountryName { get; private set; } = new();

private CountryName countryName = new();
public CountryName CountryName {
get => countryName;
init => countryName = value;
}

public string Flag { get; private set; } = "";
public CountryType CountryType { get; private set; } = CountryType.real;
public ulong? CapitalProvinceId { get; private set; }
Expand Down
4 changes: 2 additions & 2 deletions ImperatorToCK3/Imperator/Countries/CountryFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ internal sealed partial class Country {
private static void RegisterCountryKeywords(Parser parser, Country parsedCountry) {
var colorFactory = new ColorFactory();

parser.RegisterKeyword("tag", reader => parsedCountry.Tag = reader.GetString());
parser.RegisterKeyword("tag", reader => parsedCountry.tag = reader.GetString());
parser.RegisterKeyword("historical", reader => parsedCountry.HistoricalTag = reader.GetString());
parser.RegisterKeyword("origin", reader => parsedCountry.parsedOriginCountryId = reader.GetULong());
parser.RegisterKeyword("country_name", reader => parsedCountry.CountryName = CountryName.Parse(reader));
parser.RegisterKeyword("country_name", reader => parsedCountry.countryName = CountryName.Parse(reader));
parser.RegisterKeyword("flag", reader => parsedCountry.Flag = reader.GetString());

parser.RegisterKeyword("country_type", reader => SetCountryType(reader, parsedCountry));
Expand Down
27 changes: 12 additions & 15 deletions ImperatorToCK3/Imperator/Countries/CountryName.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
using commonItems;
using commonItems.Localization;
using System;
using System.Linq;

namespace ImperatorToCK3.Imperator.Countries;

internal sealed class CountryName : ICloneable {
public string Name { get; private set; } = "";
internal sealed class CountryName {
public string Name { get; init; } = "";
private string? adjective;
public CountryName? BaseName { get; private set; }
public CountryName? BaseName { get; private init; }

public object Clone() {
return new CountryName {Name = Name, adjective = adjective, BaseName = BaseName};
}

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

public static CountryName Parse(BufferedReader reader) {
var countryName = new CountryName();

string parsedName = string.Empty;
string? parsedAdjective = null;
CountryName? parsedBaseName = null;

var parser = new Parser();
parser.RegisterKeyword("name", r => countryName.Name = r.GetString());
parser.RegisterKeyword("adjective", r => countryName.adjective = r.GetString());
parser.RegisterKeyword("name", r => parsedName = r.GetString());
parser.RegisterKeyword("adjective", r => parsedAdjective = r.GetString());
parser.RegisterKeyword("base", r => {
var tempCountryName = (CountryName)countryName.Clone();
tempCountryName.BaseName = Parse(r);
countryName = (CountryName)tempCountryName.Clone();
parsedBaseName = Parse(r);
});
parser.IgnoreAndLogUnregisteredItems();
parser.ParseStream(reader);
return countryName;

return new() {Name = parsedName, adjective = parsedAdjective, BaseName = parsedBaseName};
}
}
17 changes: 7 additions & 10 deletions ImperatorToCK3/Mappers/TagTitle/TagTitleMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,6 @@ public void RegisterGovernorship(string imperatorRegion, string imperatorCountry
usedTitles.Add(ck3Title);
}
public string? GetTitleForTag(Country country, string localizedTitleName, TitleRank maxTitleRank) {
// If country has an origin (e.g. rebelled from another country), the historical tag probably points to the original country.
string tagForMapping = country.OriginCountry is not null ? country.Tag : country.HistoricalTag;

// The only case where we fail is on invalid invocation. Otherwise, failure is not an option!
if (string.IsNullOrEmpty(tagForMapping)) {
return null;
}

// Look up register.
if (registeredCountryTitles.TryGetValue(country.Id, out var titleToReturn)) {
return titleToReturn;
Expand All @@ -52,7 +44,7 @@ public void RegisterGovernorship(string imperatorRegion, string imperatorCountry
// Attempt a title match.
var rank = EnumHelper.Min(GetCK3TitleRank(country, localizedTitleName), maxTitleRank);
foreach (var mapping in titleMappings) {
var match = mapping.RankMatch(tagForMapping, rank, maxTitleRank);
var match = mapping.RankMatch(country, rank, maxTitleRank);
if (match is not null) {
if (usedTitles.Contains(match)) {
continue;
Expand Down Expand Up @@ -244,7 +236,12 @@ private string GenerateNewTitleId(Country country, string localizedTitleName, Ti

var ck3TitleId = GetTitlePrefixForRank(ck3Rank);
ck3TitleId += GeneratedCK3TitlePrefix;
ck3TitleId += country.Tag;

if (string.IsNullOrEmpty(country.Tag)) {
ck3TitleId += $"id_{country.Id}";
} else {
ck3TitleId += country.Tag;
}

return ck3TitleId;
}
Expand Down
21 changes: 17 additions & 4 deletions ImperatorToCK3/Mappers/TagTitle/TitleMapping.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
using commonItems;
using ImperatorToCK3.CK3.Titles;
using ImperatorToCK3.Imperator.Countries;
using ImperatorToCK3.Imperator.Jobs;
using ImperatorToCK3.Imperator.Provinces;
using ImperatorToCK3.Mappers.Province;
using Open.Collections;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace ImperatorToCK3.Mappers.TagTitle;

internal sealed class TitleMapping {
public string? RankMatch(string irTagOrRegion, TitleRank rank, TitleRank maxTitleRank) {
if (imperatorTagOrRegion != irTagOrRegion) {
public string? RankMatch(Country country, TitleRank rank, TitleRank maxTitleRank) {
// If country has an origin (e.g. rebelled from another country), the historical tag probably points to the original country.
string tagForMapping = country.OriginCountry is not null ? country.Tag : country.HistoricalTag;

// The mapping should have at least an I:R tag or I:R name keys.
if (imperatorTagOrRegion is null & irNameKeys.Count == 0) {
return null;
}

if (imperatorTagOrRegion is not null & imperatorTagOrRegion != tagForMapping) {
return null;
}
if (maxTitleRank < CK3TitleRank) {
Expand All @@ -22,6 +30,9 @@ internal sealed class TitleMapping {
if (ranks.Count > 0 && !ranks.Contains(rank)) {
return null;
}
if (irNameKeys.Count > 0 && !irNameKeys.Contains(country.CountryName.Name)) {
return null;
}
return ck3TitleId;
}

Expand Down Expand Up @@ -60,8 +71,9 @@ internal sealed class TitleMapping {
}

private string ck3TitleId = string.Empty;
private string imperatorTagOrRegion = string.Empty;
private string? imperatorTagOrRegion;
private readonly SortedSet<TitleRank> ranks = [];
private readonly SortedSet<string> irNameKeys = [];

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

Expand All @@ -74,6 +86,7 @@ static TitleMapping() {
var ranksToAdd = reader.GetString().ToCharArray().Select(TitleRankUtils.CharToTitleRank);
mappingToReturn.ranks.AddRange(ranksToAdd);
});
parser.RegisterKeyword("ir_name_key", reader => mappingToReturn.irNameKeys.Add(reader.GetString()));
parser.IgnoreAndLogUnregisteredItems();
}
public static TitleMapping Parse(BufferedReader reader) {
Expand Down

0 comments on commit 4e7f038

Please sign in to comment.