Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some titles being localized as "[GetCountry('MRY').Custom('get_mry_adj')] Revolt" or "[GetCountry('PRY').Custom('get_pry_adj')] Revolt" #2160

Merged
merged 4 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using commonItems;
using commonItems.Localization;
using ImperatorToCK3.Imperator.Characters;
using ImperatorToCK3.Imperator.Countries;
using ImperatorToCK3.Imperator.Families;
using Xunit;

namespace ImperatorToCK3.UnitTests.Imperator.Countries;
Expand Down Expand Up @@ -129,4 +132,74 @@ public void GetNameLocBlockReturnsCorrectLocForRevolts() {
locBlock2["english"] = "Roman";
Assert.Equal("Roman Revolt", countryName.GetNameLocBlock(locDB, [])!["english"]);
}

[Fact]
public void DataTypesInCountryNamesAreReplaced() {
var reader = new BufferedReader(
"""
name="CIVILWAR_FACTION_NAME"
adjective="CIVILWAR_FACTION_ADJECTIVE"
base={
name="PRY_DYN"
adjective="PRY_DYN_ADJ"
}
"""
);

var countryName = ImperatorToCK3.Imperator.Countries.CountryName.Parse(reader);

var locDB = new LocDB("english");
var civilWarLocBlock = locDB.AddLocBlock("CIVILWAR_FACTION_NAME");
civilWarLocBlock["english"] = "$ADJ$ Revolt";
var pryAdjLocBlock = locDB.AddLocBlock("PRY_DYN_ADJ");
pryAdjLocBlock["english"] = "[GetCountry('PRY').Custom('get_pry_adj')]";
var antigonidPryAdjLocBlock = locDB.AddLocBlock("get_pry_adj_fetch"); // used when the PRY monarch family is Antigonid
antigonidPryAdjLocBlock["english"] = "Antigonid";
var fallbackPryAdjLocBlock = locDB.AddLocBlock("get_pry_adj_fallback"); // used when the PRY monarch family is not Antigonid
fallbackPryAdjLocBlock["english"] = "Phrygian";

Assert.Equal("Phrygian Revolt", countryName.GetNameLocBlock(locDB, [])!["english"]);
}

[Fact]
public void DataTypesInCountryAdjectivesAreReplaced() {
var reader = new BufferedReader(
"""
name="CIVILWAR_FACTION_NAME"
adjective="CIVILWAR_FACTION_ADJECTIVE"
base={
name="PRY_DYN"
adjective="PRY_DYN_ADJ"
}
"""
);
var countryName = ImperatorToCK3.Imperator.Countries.CountryName.Parse(reader);

var locDB = new LocDB("english");
var civilWarAdjLocBlock = locDB.AddLocBlock("CIVILWAR_FACTION_ADJECTIVE");
civilWarAdjLocBlock["english"] = "$ADJ$";
var pryAdjLocBlock = locDB.AddLocBlock("PRY_DYN_ADJ");
pryAdjLocBlock["english"] = "[GetCountry('PRY').Custom('get_pry_adj')]";
var antigonidPryAdjLocBlock = locDB.AddLocBlock("get_pry_adj_fetch");
antigonidPryAdjLocBlock["english"] = "Antigonid";
var fallbackPryAdjLocBlock = locDB.AddLocBlock("get_pry_adj_fallback");
fallbackPryAdjLocBlock["english"] = "Phrygian";

Assert.Equal("Phrygian", countryName.GetAdjectiveLocBlock(locDB, [])!["english"]);

// Check if get_pry_adj_fetch is used instead of get_pry_adj_fallback when the monarch family is Antigonid.
var families = new FamilyCollection();
families.LoadFamilies(new BufferedReader("1 = { key=\"Antigonid\" }"));

var characters = new CharacterCollection();
characters.LoadCharacters(new BufferedReader("1 = { family=1 country=1 }"));
characters.LinkFamilies(families);

var countries = new CountryCollection();
var phrygia = Country.Parse(new BufferedReader("{ tag=PRY monarch=1 }"), 1);
countries.Add(phrygia);
characters.LinkCountries(countries);

Assert.Equal("Antigonid", countryName.GetAdjectiveLocBlock(locDB, countries)!["english"]);
}
}
4 changes: 2 additions & 2 deletions ImperatorToCK3/CK3/Titles/LandedTitles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -517,168 +517,168 @@
}
}

public void ImportImperatorHoldings(ProvinceCollection ck3Provinces, Imperator.Characters.CharacterCollection irCharacters, Date conversionDate) {
Logger.Info("Importing Imperator holdings...");
var counter = 0;

var highLevelTitlesThatHaveHolders = this
.Where(t => t.Rank >= TitleRank.duchy && t.GetHolderId(conversionDate) != "0")
.ToImmutableList();
var highLevelTitleCapitalBaronyIds = highLevelTitlesThatHaveHolders
.Select(t=>t.CapitalCounty?.CapitalBaronyId ?? t.CapitalBaronyId)
.ToImmutableHashSet();

// Dukes and above should be excluded from having their holdings converted.
// Otherwise, governors with holdings would own parts of other governorships.
var dukeAndAboveIds = highLevelTitlesThatHaveHolders
.Where(t => t.Rank >= TitleRank.duchy)
.Select(t => t.GetHolderId(conversionDate))
.ToImmutableHashSet();

// We exclude baronies that are capitals of duchies and above.
var eligibleBaronies = this
.Where(t => t.Rank == TitleRank.barony)
.Where(b => !highLevelTitleCapitalBaronyIds.Contains(b.Id))
.ToArray();

var countyCapitalBaronies = eligibleBaronies
.Where(b => b.DeJureLiege?.CapitalBaronyId == b.Id)
.OrderBy(b => b.Id)
.ToArray();

var nonCapitalBaronies = eligibleBaronies.Except(countyCapitalBaronies).OrderBy(b => b.Id).ToArray();


// In CK3, a county holder shouldn't own baronies in counties that are not their own.
// This dictionary tracks what counties are held by what characters.
Dictionary<string, HashSet<string>> countiesPerCharacter = []; // characterId -> countyIds

// Evaluate all capital baronies first (we want to distribute counties first, then baronies).
foreach (var barony in countyCapitalBaronies) {
var ck3Province = GetBaronyProvince(barony);
if (ck3Province is null) {
continue;
}

// Skip none holdings and temple holdings.
if (ck3Province.GetHoldingType(conversionDate) is "church_holding" or "none") {
continue;
}

var irProvince = ck3Province.PrimaryImperatorProvince; // TODO: when the holding owner of the primary I:R province is not able to hold the CK3 equivalent, also check the holding owners from secondary source provinces
var ck3Owner = GetEligibleCK3OwnerForImperatorProvince(irProvince);
if (ck3Owner is null) {
continue;
}

var realm = ck3Owner.ImperatorCharacter?.HomeCountry?.CK3Title;
var deFactoLiege = realm;
if (realm is not null) {
var deJureDuchy = barony.DeJureLiege?.DeJureLiege;
if (deJureDuchy is not null && deJureDuchy.GetHolderId(conversionDate) != "0" && deJureDuchy.GetTopRealm(conversionDate) == realm) {
deFactoLiege = deJureDuchy;
} else {
var deJureKingdom = deJureDuchy?.DeJureLiege;
if (deJureKingdom is not null && deJureKingdom.GetHolderId(conversionDate) != "0" && deJureKingdom.GetTopRealm(conversionDate) == realm) {
deFactoLiege = deJureKingdom;
}
}
}

// Barony is a county capital, so set the county holder to the holding owner.
var county = barony.DeJureLiege;
if (county is null) {
Logger.Warn($"County capital barony {barony.Id} has no de jure county!");
continue;
}
county.SetHolder(ck3Owner, conversionDate);
county.SetDeFactoLiege(deFactoLiege, conversionDate);

if (!countiesPerCharacter.TryGetValue(ck3Owner.Id, out var countyIds)) {
countyIds = [];
countiesPerCharacter[ck3Owner.Id] = countyIds;
}
countyIds.Add(county.Id);

++counter;
}

// In CK3, a baron that doesn't own counties can only hold a single barony.
// This dictionary IDs of such barons that already hold a barony.
HashSet<string> baronyHolderIds = [];

// After all possible county capital baronies are distributed, distribute the rest of the eligible baronies.
foreach (var barony in nonCapitalBaronies) {
var ck3Province = GetBaronyProvince(barony);
if (ck3Province is null) {
continue;
}

// Skip none holdings and temple holdings.
if (ck3Province.GetHoldingType(conversionDate) is "church_holding" or "none") {
continue;
}

var irProvince = ck3Province.PrimaryImperatorProvince; // TODO: when the holding owner of the primary I:R province is not able to hold the CK3 equivalent, also check the holding owners from secondary source provinces
var ck3Owner = GetEligibleCK3OwnerForImperatorProvince(irProvince);
if (ck3Owner is null) {
continue;
}
if (baronyHolderIds.Contains(ck3Owner.Id)) {
continue;
}

var county = barony.DeJureLiege;
if (county is null) {
Logger.Warn($"Barony {barony.Id} has no de jure county!");
continue;
}
// A non-capital barony cannot be held by a character that owns a county but not the county the barony is in.
if (countiesPerCharacter.TryGetValue(ck3Owner.Id, out var countyIds) && !countyIds.Contains(county.Id)) {
continue;
}

barony.SetHolder(ck3Owner, conversionDate);
// No need to set de facto liege for baronies, they are tied to counties.

baronyHolderIds.Add(ck3Owner.Id);

++counter;
}
Logger.Info($"Imported {counter} holdings from I:R.");
Logger.IncrementProgress();
return;

Province? GetBaronyProvince(Title barony) {
var ck3ProvinceId = barony.ProvinceId;
if (ck3ProvinceId is null) {
return null;
}
if (!ck3Provinces.TryGetValue(ck3ProvinceId.Value, out var ck3Province)) {
return null;
}
return ck3Province;
}

Character? GetEligibleCK3OwnerForImperatorProvince(Imperator.Provinces.Province? irProvince) {
var holdingOwnerId = irProvince?.HoldingOwnerId;
if (holdingOwnerId is null) {
return null;
}

var irOwner = irCharacters[holdingOwnerId.Value];
var ck3Owner = irOwner.CK3Character;
if (ck3Owner is null) {
return null;
}
if (dukeAndAboveIds.Contains(ck3Owner.Id)) {
return null;
}

return ck3Owner;
}
}

Check notice on line 681 in ImperatorToCK3/CK3/Titles/LandedTitles.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/Titles/LandedTitles.cs#L520-L681

Complex Method
public void RemoveInvalidLandlessTitles(Date ck3BookmarkDate) {
Logger.Info("Removing invalid landless titles...");
var removedGeneratedTitles = new HashSet<string>();
Expand Down Expand Up @@ -744,9 +744,9 @@
);

var kingdomAdjLoc = ck3LocDB.GetOrCreateLocBlock(kingdom.Id + "_adj");
string duchyAdjLocKey = duchy.Id + "_adj"; // TODO: add some handling for the case where this is not localized
kingdomAdjLoc.ModifyForEveryLanguage(
(orig, language) => $"${duchy.Id}_adj$"
);
(orig, language) => $"${duchyAdjLocKey}$");

Check notice on line 749 in ImperatorToCK3/CK3/Titles/LandedTitles.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

ImperatorToCK3/CK3/Titles/LandedTitles.cs#L749

'orig' is not used. Use discard parameter instead.

kingdom.DeJureLiege = capitalEmpireRealm;
duchy.DeJureLiege = kingdom;
Expand Down Expand Up @@ -964,145 +964,145 @@
return newEmpire;
}

private void SplitDisconnectedEmpires(
IDictionary<string, ConcurrentHashSet<string>> kingdomAdjacenciesByLand,
IDictionary<string, ConcurrentHashSet<string>> kingdomAdjacenciesByWaterBody,
HashSet<string> removableEmpireIds,
Dictionary<string, ImmutableArray<Pillar>> kingdomToDominantHeritagesDict,
Dictionary<string, Title> heritageToEmpireDict,
CK3LocDB ck3LocDB,
Date date
) {
Logger.Debug("Splitting disconnected empires...");

// Combine kingdom adjacencies by land and water body into a single dictionary.
var kingdomAdjacencies = new Dictionary<string, HashSet<string>>();
foreach (var (kingdomId, adjacencies) in kingdomAdjacenciesByLand) {
kingdomAdjacencies[kingdomId] = [..adjacencies];
}
foreach (var (kingdomId, adjacencies) in kingdomAdjacenciesByWaterBody) {
if (!kingdomAdjacencies.TryGetValue(kingdomId, out var set)) {
set = [];
kingdomAdjacencies[kingdomId] = set;
}
set.UnionWith(adjacencies);
}

// If one separated kingdom is separated from the rest of its de jure empire, try to get the second dominant heritage in the kingdom.
// If any neighboring kingdom has that heritage as dominant one, transfer the separated kingdom to the neighboring kingdom's empire.
var disconnectedEmpiresDict = GetDictOfDisconnectedEmpires(kingdomAdjacencies, removableEmpireIds);
if (disconnectedEmpiresDict.Count == 0) {
return;
}
Logger.Debug("\tTransferring stranded kingdoms to neighboring empires...");
foreach (var (empire, kingdomGroups) in disconnectedEmpiresDict) {
var dissolvableGroups = kingdomGroups.Where(g => g.Count == 1).ToArray();
foreach (var group in dissolvableGroups) {
var kingdom = group.First();
if (!kingdomToDominantHeritagesDict.TryGetValue(kingdom.Id, out var dominantHeritages)) {
continue;
}
if (dominantHeritages.Length < 2) {
continue;
}

var adjacentEmpiresByLand = kingdomAdjacenciesByLand[kingdom.Id].Select(k => this[k].DeJureLiege)
.Where(e => e is not null)
.Select(e => e!)
.ToHashSet();

// Try to find valid neighbor by land first, to reduce the number of exclaves.
Title? validNeighbor = null;
foreach (var secondaryHeritage in dominantHeritages.Skip(1)) {
if (!heritageToEmpireDict.TryGetValue(secondaryHeritage.Id, out var heritageEmpire)) {
continue;
}
if (!adjacentEmpiresByLand.Contains(heritageEmpire)) {
continue;
}

validNeighbor = heritageEmpire;
Logger.Debug($"\t\tTransferring kingdom {kingdom.Id} from empire {empire.Id} to empire {validNeighbor.Id} neighboring by land.");
break;
}

// If no valid neighbor by land, try to find valid neighbor by water.
if (validNeighbor is null) {
var adjacentEmpiresByWaterBody = kingdomAdjacenciesByWaterBody[kingdom.Id].Select(k => this[k].DeJureLiege)
.Where(e => e is not null)
.Select(e => e!)
.ToHashSet();

foreach (var secondaryHeritage in dominantHeritages.Skip(1)) {
if (!heritageToEmpireDict.TryGetValue(secondaryHeritage.Id, out var heritageEmpire)) {
continue;
}
if (!adjacentEmpiresByWaterBody.Contains(heritageEmpire)) {
continue;
}

validNeighbor = heritageEmpire;
Logger.Debug($"\t\tTransferring kingdom {kingdom.Id} from empire {empire.Id} to empire {validNeighbor.Id} neighboring by water body.");
break;
}
}

if (validNeighbor is not null) {
kingdom.DeJureLiege = validNeighbor;
}
}
}

disconnectedEmpiresDict = GetDictOfDisconnectedEmpires(kingdomAdjacencies, removableEmpireIds);
if (disconnectedEmpiresDict.Count == 0) {
return;
}
Logger.Debug("\tCreating new empires for disconnected groups...");
foreach (var (empire, groups) in disconnectedEmpiresDict) {
// Keep the largest group as is, and create new empires based on most developed counties for the rest.
var largestGroup = groups.MaxBy(g => g.Count);
foreach (var group in groups) {
if (group == largestGroup) {
continue;
}

var mostDevelopedCounty = group
.SelectMany(k => k.GetDeJureVassalsAndBelow("c").Values)
.MaxBy(c => c.GetOwnOrInheritedDevelopmentLevel(date));
if (mostDevelopedCounty is null) {
continue;
}

string newEmpireId = $"e_IRTOCK3_from_{mostDevelopedCounty.Id}";
var newEmpire = Add(newEmpireId);
newEmpire.Color1 = mostDevelopedCounty.Color1;
newEmpire.CapitalCounty = mostDevelopedCounty;
newEmpire.HasDefiniteForm = false;

var empireNameLoc = ck3LocDB.GetOrCreateLocBlock(newEmpireId);
empireNameLoc.ModifyForEveryLanguage(
(orig, language) => $"${mostDevelopedCounty.Id}$"
);

var empireAdjLoc = ck3LocDB.GetOrCreateLocBlock(newEmpireId + "_adj");
empireAdjLoc.ModifyForEveryLanguage(
(orig, language) => $"${mostDevelopedCounty.Id}_adj$"
);

foreach (var kingdom in group) {
kingdom.DeJureLiege = newEmpire;
}

Logger.Debug($"\t\tCreated new empire {newEmpire.Id} for group {string.Join(',', group.Select(k => k.Id))}.");
}
}

disconnectedEmpiresDict = GetDictOfDisconnectedEmpires(kingdomAdjacencies, removableEmpireIds);
if (disconnectedEmpiresDict.Count > 0) {
Logger.Warn("Failed to split some disconnected empires: " + string.Join(", ", disconnectedEmpiresDict.Keys.Select(e => e.Id)));
}
}

Check notice on line 1105 in ImperatorToCK3/CK3/Titles/LandedTitles.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/Titles/LandedTitles.cs#L967-L1105

Complex Method
private Dictionary<Title, List<HashSet<Title>>> GetDictOfDisconnectedEmpires(
Dictionary<string, HashSet<string>> kingdomAdjacencies,
IReadOnlySet<string> removableEmpireIds
Expand Down
1 change: 1 addition & 0 deletions ImperatorToCK3/CK3/Titles/Title.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,123 +172,123 @@
vassal.DeJureLiege = this;
}
}
public void InitializeFromTag(
Country country,
Dependency? dependency,
CountryCollection imperatorCountries,
LocDB irLocDB,
CK3LocDB ck3LocDB,
ProvinceMapper provinceMapper,
CoaMapper coaMapper,
GovernmentMapper governmentMapper,
SuccessionLawMapper successionLawMapper,
DefiniteFormMapper definiteFormMapper,
ReligionMapper religionMapper,
CultureMapper cultureMapper,
NicknameMapper nicknameMapper,
CharacterCollection characters,
Date conversionDate,
Configuration config
) {
ImperatorCountry = country;
ImperatorCountry.CK3Title = this;

LocBlock? validatedName = GetValidatedName(country, imperatorCountries, irLocDB);

HasDefiniteForm = definiteFormMapper.IsDefiniteForm(ImperatorCountry.Name);
RulerUsesTitleName = false;

PlayerCountry = ImperatorCountry.PlayerCountry;

ClearHolderSpecificHistory();

FillHolderAndGovernmentHistory(country, characters, governmentMapper, irLocDB, ck3LocDB, religionMapper, cultureMapper, nicknameMapper, provinceMapper, config, conversionDate);

// Determine color.
var color1Opt = ImperatorCountry.Color1;
if (color1Opt is not null) {
Color1 = color1Opt;
}

// determine successions laws
History.AddFieldValue(conversionDate,
"succession_laws",
"succession_laws",
successionLawMapper.GetCK3LawsForImperatorLaws(ImperatorCountry.GetLaws())
);

// Determine CoA.
if (IsCreatedFromImperator || !config.UseCK3Flags) {
CoA = coaMapper.GetCoaForFlagName(ImperatorCountry.Flag);
}

// Determine other attributes:
// Set capital to Imperator tag's capital.
if (ImperatorCountry.CapitalProvinceId is not null) {
var srcCapital = ImperatorCountry.CapitalProvinceId.Value;
foreach (var ck3ProvId in provinceMapper.GetCK3ProvinceNumbers(srcCapital)) {
var foundCounty = parentCollection.GetCountyForProvince(ck3ProvId);
if (foundCounty is null) {
continue;
}

// If the title is a de jure duchy, potential capital must be within it.
if (Rank == TitleRank.duchy && DeJureVassals.Count > 0 && foundCounty.DeJureLiege?.Id != Id) {
continue;
}

CapitalCounty = foundCounty;
break;
}
}

// determine country name localization
var nameSet = false;
if (validatedName is not null) {
var nameLocBlock = ck3LocDB.GetOrCreateLocBlock(Id);
nameLocBlock.CopyFrom(validatedName);
nameSet = true;
}
if (!nameSet) {
var irTagLoc = irLocDB.GetLocBlockForKey(ImperatorCountry.Tag);
if (irTagLoc is not null) {
var nameLocBlock = ck3LocDB.GetOrCreateLocBlock(Id);
nameLocBlock.CopyFrom(irTagLoc);
nameSet = true;
}
}
if (!nameSet) {
// use unlocalized name if not empty
var name = ImperatorCountry.Name;
if (!string.IsNullOrEmpty(name)) {
Logger.Warn($"Using unlocalized Imperator name {name} as name for {Id}!");
var nameLocBlock = ck3LocDB.GetOrCreateLocBlock(Id);
nameLocBlock[ConverterGlobals.PrimaryLanguage] = name;
nameSet = true;
}
}
// giving up
if (!nameSet) {
Logger.Warn($"{Id} needs help with localization! {ImperatorCountry.Name}?");
}

// determine adjective localization
TrySetAdjectiveLoc(irLocDB, imperatorCountries, ck3LocDB);

// If country is a subject, convert it to a vassal.
if (dependency is not null) {
var overLordTitle = imperatorCountries[dependency.OverlordId].CK3Title;
if (overLordTitle is null) {
Logger.Warn($"Can't find CK3 title for country {dependency.OverlordId}, overlord of {country.Id}.");
}
DeJureLiege = overLordTitle;
SetDeFactoLiege(overLordTitle, dependency.StartDate);
}
}

/// <summary>
/// Fills title's history with Imperator and pre-Imperator rulers and sets appropriate government.
/// </summary>

Check notice on line 291 in ImperatorToCK3/CK3/Titles/Title.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/Titles/Title.cs#L175-L291

Complex Method
private void FillHolderAndGovernmentHistory(Country imperatorCountry,
CharacterCollection characters,
GovernmentMapper governmentMapper,
Expand Down Expand Up @@ -590,96 +590,96 @@
}
}

private void TrySetNameFromGovernorship(
Governorship governorship,
ImperatorRegionMapper irRegionMapper,
Country country,
Imperator.Provinces.ProvinceCollection irProvinces,
bool regionHasMultipleGovernorships,
LocDB irLocDB,
CK3LocDB ck3LocDB
) {
if (ck3LocDB.ContainsKey(Id)) {
return;
}

var nameSet = false;
var regionId = governorship.Region.Id;
irRegionMapper.Regions.TryGetValue(regionId, out var region);
LocBlock? regionLocBlock = irLocDB.GetLocBlockForKey(regionId);

// If any area in the region is at least 60% owned, use the area name for governorship name.
if (regionHasMultipleGovernorships && region is not null) {
Area? potentialSourceArea = null;
float biggestOwnershipPercentage = 0f;
foreach (var area in region.Areas) {
var areaProvinces = area.Provinces;
if (areaProvinces.Count == 0) {
continue;
}
var controlledProvinces = areaProvinces.Where(p => country.Equals(p.OwnerCountry));
var ownershipPercentage = (float)controlledProvinces.Count() / areaProvinces.Count;
if (ownershipPercentage < 0.6) {
continue;
}
if (ownershipPercentage > biggestOwnershipPercentage) {
potentialSourceArea = area;
biggestOwnershipPercentage = ownershipPercentage;
}
}

if (potentialSourceArea is not null && irLocDB.TryGetValue(potentialSourceArea.Id, out var areaLocBlock)) {
Logger.Debug($"Naming {Id} after I:R area {potentialSourceArea.Id} majorly ({biggestOwnershipPercentage:P}) controlled by {country.Tag}...");
var nameLocBlock = ck3LocDB.GetOrCreateLocBlock(Id);
nameLocBlock.CopyFrom(areaLocBlock);
nameSet = true;

var adjLocBlock = ck3LocDB.GetOrCreateLocBlock($"{Id}_adj");
adjLocBlock.CopyFrom(nameLocBlock);
adjLocBlock.ModifyForEveryLanguage((loc, language) => language == "english" ? loc?.GetAdjective() : loc);
}
}
// Try to use the name of most developed owned territory in the region.
if (!nameSet && regionHasMultipleGovernorships && region is not null) {
var sourceProvince = irProvinces
.Where(p => region.ContainsProvince(p.Id) && country.Equals(p.OwnerCountry))
.MaxBy(p => p.CivilizationValue);
if (sourceProvince is not null && irLocDB.TryGetValue(sourceProvince.Name, out var provinceLocBlock)) {
Logger.Debug($"Naming {Id} after most developed I:R territory: {sourceProvince.Id}...");
var nameLocBlock = ck3LocDB.GetOrCreateLocBlock(Id);
nameLocBlock.CopyFrom(provinceLocBlock);
nameSet = true;

var adjLocBlock = ck3LocDB.GetOrCreateLocBlock($"{Id}_adj");
adjLocBlock.CopyFrom(nameLocBlock);
adjLocBlock.ModifyForEveryLanguage((loc, language) => language == "english" ? loc?.GetAdjective() : loc);
}
}
// Try to use "<country adjective> <region name>" as governorship name if region has multiple governorships.
// Example: Mauretania -> Roman Mauretania
if (!nameSet && regionHasMultipleGovernorships && regionLocBlock is not null) {
var ck3Country = country.CK3Title;
if (ck3Country is not null && ck3LocDB.TryGetValue($"{ck3Country.Id}_adj", out var countryAdjectiveLocBlock)) {
Logger.Debug($"Naming {Id} after governorship with country adjective: {country.Tag} {governorship.Region.Id}...");
var nameLocBlock = ck3LocDB.GetOrCreateLocBlock(Id);
nameLocBlock.CopyFrom(regionLocBlock);
nameLocBlock.ModifyForEveryLanguage(countryAdjectiveLocBlock,
(orig, adj, _) => $"{adj} {orig}"
);
nameSet = true;
}
}
if (!nameSet && regionLocBlock is not null) {
Logger.Debug($"Naming {Id} after governorship: {governorship.Region.Id}...");
var nameLocBlock = ck3LocDB.GetOrCreateLocBlock(Id);
nameLocBlock.CopyFrom(regionLocBlock);
nameSet = true;
}
if (!nameSet && Id.Contains("_IRTOCK3_")) {
Logger.Warn($"{Id} needs help with localization!");
}
}

Check notice on line 682 in ImperatorToCK3/CK3/Titles/Title.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/Titles/Title.cs#L593-L682

Complex Method
public void LoadTitles(BufferedReader reader) {
var parser = new Parser();
RegisterKeys(parser);
Expand Down Expand Up @@ -737,165 +737,166 @@
History.AddFieldValue(date, "development_level", "change_development_level", value);
}

private void TrySetAdjectiveLoc(LocDB irLocDB, CountryCollection imperatorCountries, CK3LocDB ck3LocDB) {
if (ImperatorCountry is null) {
Logger.Warn($"Cannot set adjective for CK3 title {Id} from null Imperator country!");
return;
}

var adjSet = false;
var locKey = $"{Id}_adj";

if (ImperatorCountry.Tag is "PRY" or "SEL" or "MRY") {
// these tags use customizable loc for adj
LocBlock? validatedAdj;
switch (ImperatorCountry.Name) {
case "PRY_DYN" when ImperatorCountry.Monarch?.Family?.Key == "Antigonid":
var pryAdjLocBlock = irLocDB.GetLocBlockForKey("get_pry_adj_fetch");
if (pryAdjLocBlock is not null) {
const string pryAdjKey = "PRY_ADJ";
validatedAdj = new LocBlock(pryAdjLocBlock.Id, pryAdjLocBlock);
validatedAdj.ModifyForEveryLanguage(
otherBlock: irLocDB.GetLocBlockForKey(pryAdjKey) ?? new LocBlock(pryAdjKey, ConverterGlobals.PrimaryLanguage) {
[ConverterGlobals.PrimaryLanguage] = "Antigonid"
},
(loc, modifyingLoc, language) => {
var locToReturn = loc?.Replace($"${pryAdjKey}$", modifyingLoc);
if (locToReturn is not null && locToReturn.Contains("[GetCountry(")) {
locToReturn = irLocDB.GetLocBlockForKey("get_pry_adj_fallback")?[language];
}
return locToReturn;
});
} else {
validatedAdj = pryAdjLocBlock;
}

break;
case "PRY_DYN":
validatedAdj = irLocDB.GetLocBlockForKey("get_pry_adj_fallback");
break;
case "SEL_DYN" when ImperatorCountry.Monarch?.Family?.Key == "Seleukid":
var selAdjLocBlock = irLocDB.GetLocBlockForKey("get_sel_adj_fetch");
if (selAdjLocBlock is not null) {
const string selAdjKey = "SEL_ADJ";
validatedAdj = new LocBlock(selAdjLocBlock.Id, selAdjLocBlock);
validatedAdj.ModifyForEveryLanguage(
otherBlock: irLocDB.GetLocBlockForKey(selAdjKey) ?? new LocBlock(selAdjKey, ConverterGlobals.PrimaryLanguage) {
[ConverterGlobals.PrimaryLanguage] = "Seleukid"
},
(loc, modifyingLoc, language) => {
var locToReturn = loc?.Replace($"${selAdjKey}$", modifyingLoc);
if (locToReturn is not null && locToReturn.Contains("[GetCountry(")) {
locToReturn = irLocDB.GetLocBlockForKey("get_sel_adj_fallback")?[language];
}
return locToReturn;
});
} else {
validatedAdj = selAdjLocBlock;
}

break;
case "SEL_DYN":
validatedAdj = irLocDB.GetLocBlockForKey("get_sel_adj_fallback");
break;
case "MRY_DYN" when ImperatorCountry.Monarch?.Family?.Key == "Maurya":
var mryAdjLocBlock = irLocDB.GetLocBlockForKey("get_mry_adj_fetch");
if (mryAdjLocBlock is not null) {
const string mryAdjKey = "MRY_ADJ";
validatedAdj = new LocBlock(mryAdjLocBlock.Id, mryAdjLocBlock);
validatedAdj.ModifyForEveryLanguage(
otherBlock: irLocDB.GetLocBlockForKey(mryAdjKey) ?? new LocBlock(mryAdjKey, ConverterGlobals.PrimaryLanguage) {
[ConverterGlobals.PrimaryLanguage] = "Mauryan"
},
(loc, modifyingLoc, language) => {
var locToReturn = loc?.Replace($"${mryAdjKey}$", modifyingLoc);
if (locToReturn is not null && locToReturn.Contains("[GetCountry(")) {
locToReturn = irLocDB.GetLocBlockForKey("get_mry_adj_fallback")?[language];
}
return locToReturn;
});
} else {
validatedAdj = mryAdjLocBlock;
}
break;
case "MRY_DYN":
validatedAdj = irLocDB.GetLocBlockForKey("get_mry_adj_fallback");
break;
default:
validatedAdj = null;
break;
}

if (validatedAdj is not null) {
var adjLocBlock = ck3LocDB.GetOrCreateLocBlock(locKey);
adjLocBlock.CopyFrom(validatedAdj);
adjSet = true;
}
}
if (!adjSet) {
var adjOpt = ImperatorCountry.CountryName.GetAdjectiveLocBlock(irLocDB, imperatorCountries);
if (adjOpt is not null) {
var adjLocBlock = ck3LocDB.GetOrCreateLocBlock(locKey);
adjLocBlock.CopyFrom(adjOpt);
adjSet = true;
}
}
if (!adjSet) {
// Try to use the country name as adjective.
var adjLocalizationMatch = irLocDB.GetLocBlockForKey(ImperatorCountry.Tag);
if (adjLocalizationMatch is not null) {
var adjLocBlock = ck3LocDB.GetOrCreateLocBlock(locKey);
adjLocBlock.CopyFrom(adjLocalizationMatch);
adjSet = true;
}
}

// Try to generate English adjective from country name.
if (!adjSet) {
if (ck3LocDB.TryGetValue(Id, out var nameLocBlock) && nameLocBlock["english"] is {} name) {
// If name has 3 characters and last 2 characters are digits, it's probably a raw Imperator tag.
// In that case, we don't want to use it as a base for adjective.
if (!(name.Length == 3 && char.IsDigit(name[1]) && char.IsDigit(name[2]))) {
var generatedAdjective = name.GetAdjective();
Logger.Debug($"Generated adjective for country \"{name}\": \"{generatedAdjective}\"");

var adjLocBlock = ck3LocDB.GetOrCreateLocBlock(locKey);
adjLocBlock["english"] = generatedAdjective;
adjSet = true;
}
}
}

if (!adjSet) {
// Use unlocalized name if not empty
var name = ImperatorCountry.Name;
if (!string.IsNullOrEmpty(name)) {
Logger.Warn($"Using unlocalized Imperator name {name} as adjective for {Id}!");
var adjLocBlock = ck3LocDB.GetOrCreateLocBlock(locKey);
adjLocBlock[ConverterGlobals.PrimaryLanguage] = name;
adjSet = true;
}
}

// Give up.
if (!adjSet) {
Logger.Warn($"{Id} needs help with localization for adjective! {ImperatorCountry.Name}_adj?");
}
}
[commonItems.Serialization.NonSerialized] public string? CoA { get; private set; }

[SerializedName("capital")] public string? CapitalCountyId { get; private set; }
[commonItems.Serialization.NonSerialized]
public Title? CapitalCounty {
get {
if (CapitalCountyId is null) {
return null;
}
if (parentCollection.TryGetValue(CapitalCountyId, out var capitalCounty)) {
return capitalCounty;
}
Logger.Warn($"Capital county {CapitalCountyId} of {Id} not found!");
return null;
}

Check notice on line 899 in ImperatorToCK3/CK3/Titles/Title.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/Titles/Title.cs#L740-L899

Complex Method

private set => CapitalCountyId = value?.Id;
}
Expand Down Expand Up @@ -1335,108 +1336,108 @@
foreach (var (ck3Position, sources) in councilPositionToSourcesDict) {
// The order of I:R source position types is important - the first filled one found will be used.
foreach (var sourceOfficeType in sources) {
var job = convertibleJobs.Find(o => o.OfficeType == sourceOfficeType);
if (job is null) {
continue;
}

var ck3Official = job.Character.CK3Character;
if (ck3Official is null) {
continue;
}
if (alreadyEmployedCharacters.Contains(ck3Official.Id)) {
continue;
}

// A ruler cannot be their own councillor.
if (ck3Official.Id == ck3Ruler.Id) {
continue;
}

if (!heldTitlesPerCharacterCache.TryGetValue(ck3Official.Id, out int heldTitlesCount)) {
heldTitlesCount = parentCollection.Count(t => t.GetHolderId(bookmarkDate) == ck3Official.Id);
heldTitlesPerCharacterCache[ck3Official.Id] = heldTitlesCount;
}

if (ck3Position == "councillor_court_chaplain") {
// Court chaplains need to have the same faith as the ruler.
var rulerFaithId = ck3Ruler.GetFaithId(bookmarkDate);
if (rulerFaithId is null || rulerFaithId != ck3Official.GetFaithId(bookmarkDate)) {
continue;
}

// If the faith has Disallowed Clerical Marriage, don't allow married court chaplains.
var rulerFaith = religionCollection.GetFaith(rulerFaithId);
if (rulerFaith is null) {
continue;
}
if (rulerFaith.HasDoctrine("doctrine_clerical_marriage_disallowed")) {
if (ck3Official.GetSpouseIds(bookmarkDate).Count > 0) {
continue;
}
}

// If the court faith has doctrine_theocracy_temporal (Theocratic Clerical Tradition), the court chaplain should
// be either theocratic or landless.
// For the purpose of the conversion, we simply require them to be landless.
if (rulerFaith.HasDoctrine("doctrine_theocracy_temporal")) {
if (heldTitlesCount > 0) {
continue;
}
}

// Skip if the faith doesn't allow the character's gender to be clergy.
var clerigalGenderDoctrine = rulerFaith.GetDoctrineIdForDoctrineCategoryId("doctrine_clerical_gender");
if (clerigalGenderDoctrine is not null) {
if (clerigalGenderDoctrine == "doctrine_clerical_gender_female_only" && !ck3Official.Female) {
continue;
}
if (clerigalGenderDoctrine == "doctrine_clerical_gender_male_only" && ck3Official.Female) {
continue;
}
}
} else if (ck3Position == "councillor_steward" || ck3Position == "councillor_chancellor" || ck3Position == "councillor_marshal") {
// Unless they are rulers, stewards, chancellors and marshals need to have the dominant gender of the faith.
if (heldTitlesCount == 0) {
var courtFaith = ck3Ruler.GetFaithId(bookmarkDate);
if (courtFaith is not null) {
var dominantGenderDoctrine = religionCollection.GetFaith(courtFaith)?
.GetDoctrineIdForDoctrineCategoryId("doctrine_gender");
if (dominantGenderDoctrine == "doctrine_gender_male_dominated" && ck3Official.Female) {
continue;
}
if (dominantGenderDoctrine == "doctrine_gender_female_dominated" && !ck3Official.Female) {
continue;
}
}
}
}

// We only need to set the employer when the council member is landless.
if (heldTitlesCount == 0) {
ck3Official.History.AddFieldValue(bookmarkDate, "employer", "employer", ck3Ruler.Id);
}
ck3Official.History.AddFieldValue(bookmarkDate, "council_position", "give_council_position", ck3Position);

// One character should only hold one CK3 position.
convertibleJobs.Remove(job);
alreadyEmployedCharacters.Add(ck3Official.Id);

break;
}
}
}

// used by county titles only
[commonItems.Serialization.NonSerialized] public IEnumerable<ulong> CountyProvinceIds => DeJureVassals
.Where(v => v.Rank == TitleRank.barony && v.ProvinceId.HasValue)
.Select(v => v.ProvinceId!.Value);
[commonItems.Serialization.NonSerialized] private string CapitalBaronyId { get; set; } = string.Empty; // used when parsing inside county to save first barony
[commonItems.Serialization.NonSerialized] public ulong? CapitalBaronyProvinceId { get; private set; } // county barony's province; 0 is not a valid barony ID

// used by barony titles only
[SerializedName("province")] public ulong? ProvinceId { get; private set; } // province is area on map. b_barony is its corresponding title.

public void RemoveHistoryPastDate(Date ck3BookmarkDate) {
History.RemoveHistoryPastDate(ck3BookmarkDate);

Check notice on line 1442 in ImperatorToCK3/CK3/Titles/Title.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/Titles/Title.cs#L1339-L1442

Complex Method
}
Expand Down
63 changes: 58 additions & 5 deletions ImperatorToCK3/Imperator/Countries/CountryName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,13 @@ public object Clone() {
}
var locBlockToReturn = new LocBlock(Name, directNameLocMatch);
locBlockToReturn.ModifyForEveryLanguage(baseAdjLoc,
(orig, modifying, language) => orig?.Replace("$ADJ$", modifying)
);
(orig, modifying, language) => {
string? toReturn = orig?.Replace("$ADJ$", modifying);
if (toReturn is not null) {
toReturn = ReplaceDataTypes(toReturn, language, irLocDB, imperatorCountries);
}
return toReturn;
});
return locBlockToReturn;
}

Expand Down Expand Up @@ -79,9 +84,13 @@ private LocBlock GetCompositeNameLocBlock(string[] nameParts, LocDB irLocDB) {
var baseAdjLoc = BaseName?.GetAdjectiveLocBlock(irLocDB, imperatorCountries);
if (baseAdjLoc is not null) {
var locBlockToReturn = new LocBlock(adjKey, directAdjLocMatch);
locBlockToReturn.ModifyForEveryLanguage(baseAdjLoc, (orig, modifying, language) =>
orig?.Replace("$ADJ$", modifying)
);
locBlockToReturn.ModifyForEveryLanguage(baseAdjLoc, (orig, modifying, language) => {
var toReturn = orig?.Replace("$ADJ$", modifying);
if (toReturn is not null) {
toReturn = ReplaceDataTypes(toReturn, language, irLocDB, imperatorCountries);
}
return toReturn;
});
return locBlockToReturn;
}
} else if (directAdjLocMatch is not null) {
Expand Down Expand Up @@ -109,6 +118,50 @@ public string GetAdjectiveLocKey() {
}
return Name + "_ADJ";
}

private string ReplaceDataTypes(string loc, string language, LocDB irLocDB, CountryCollection irCountries) {
if (!loc.Contains("[GetCountry(")) {
return loc;
}

const string phrygianAdj = "[GetCountry('PRY').Custom('get_pry_adj')]";
Country? pry = irCountries.FirstOrDefault(country => country.Tag == "PRY");
LocBlock? pryAdjLocBlock;
if (pry is not null && pry.Monarch?.Family?.Key == "Antigonid") {
pryAdjLocBlock = irLocDB.GetLocBlockForKey("get_pry_adj_fetch");
} else {
pryAdjLocBlock = irLocDB.GetLocBlockForKey("get_pry_adj_fallback");
}
if (pryAdjLocBlock is not null && pryAdjLocBlock.HasLocForLanguage(language)) {
loc = loc.Replace(phrygianAdj, pryAdjLocBlock[language]);
}

const string mauryanAdj = "[GetCountry('MRY').Custom('get_mry_adj')]";
Country? mry = irCountries.FirstOrDefault(country => country.Tag == "MRY");
LocBlock? mryAdjLocBlock;
if (mry is not null && mry.Monarch?.Family?.Key == "Maurya") {
mryAdjLocBlock = irLocDB.GetLocBlockForKey("get_mry_adj_fetch");
} else {
mryAdjLocBlock = irLocDB.GetLocBlockForKey("get_mry_adj_fallback");
}
if (mryAdjLocBlock is not null && mryAdjLocBlock.HasLocForLanguage(language)) {
loc = loc.Replace(mauryanAdj, mryAdjLocBlock[language]);
}

const string seleucidAdj = "[GetCountry('SEL').Custom('get_sel_adj')]";
Country? sel = irCountries.FirstOrDefault(country => country.Tag == "SEL");
LocBlock? selAdjLocBlock;
if (sel is not null && sel.Monarch?.Family?.Key == "Seleukid") {
selAdjLocBlock = irLocDB.GetLocBlockForKey("get_sel_adj_fetch");
} else {
selAdjLocBlock = irLocDB.GetLocBlockForKey("get_sel_adj_fallback");
}
if (selAdjLocBlock is not null && selAdjLocBlock.HasLocForLanguage(language)) {
loc = loc.Replace(seleucidAdj, selAdjLocBlock[language]);
}

return loc;
}

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