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

Map a few I:R inventions to CK3 innovations #1893

Merged
merged 1 commit into from
Apr 21, 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
5 changes: 3 additions & 2 deletions ImperatorToCK3/CK3/Cultures/CultureCollection.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using commonItems;
using commonItems.Collections;
using commonItems.Colors;
using commonItems.Localization;
using commonItems.Mods;
using Fernandezja.ColorHashSharp;
using ImperatorToCK3.CommonUtils;
Expand Down Expand Up @@ -175,12 +176,12 @@
return cultureMapper.Match(irCulture, ck3ProvinceId, irProvinceId, country.HistoricalTag);
}

public void ImportTechnology(CountryCollection countries, CultureMapper cultureMapper, ProvinceMapper provinceMapper, InventionsDB inventionsDB) { // TODO: test this
public void ImportTechnology(CountryCollection countries, CultureMapper cultureMapper, ProvinceMapper provinceMapper, InventionsDB inventionsDB, LocDB irLocDB) { // TODO: add tests for this

Check warning on line 179 in ImperatorToCK3/CK3/Cultures/CultureCollection.cs

View workflow job for this annotation

GitHub Actions / build (self-hosted, linux)

Check warning on line 179 in ImperatorToCK3/CK3/Cultures/CultureCollection.cs

View workflow job for this annotation

GitHub Actions / build (macos-14)

Check warning on line 179 in ImperatorToCK3/CK3/Cultures/CultureCollection.cs

View workflow job for this annotation

GitHub Actions / test (macos-14)

Check warning on line 179 in ImperatorToCK3/CK3/Cultures/CultureCollection.cs

View workflow job for this annotation

GitHub Actions / build (self-hosted, windows)

Check warning on line 179 in ImperatorToCK3/CK3/Cultures/CultureCollection.cs

View workflow job for this annotation

GitHub Actions / test (self-hosted, windows)

Check warning on line 179 in ImperatorToCK3/CK3/Cultures/CultureCollection.cs

View workflow job for this annotation

GitHub Actions / test_and_check_coverage

Logger.Info("Converting Imperator inventions to CK3 innovations...");

var innovationMapper = new InnovationMapper();
innovationMapper.LoadLinksAndBonuses("configurables/inventions_to_innovations_map.txt");
innovationMapper.LogUnmappedInventions(inventionsDB);
innovationMapper.LogUnmappedInventions(inventionsDB, irLocDB);

// Group I:R countries by corresponding CK3 culture.
var countriesByCulture = countries.Select(c => new {
Expand Down
2 changes: 1 addition & 1 deletion ImperatorToCK3/CK3/World.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@
}
}

Cultures.ImportTechnology(impWorld.Countries, cultureMapper, provinceMapper, impWorld.InventionsDB);
Cultures.ImportTechnology(impWorld.Countries, cultureMapper, provinceMapper, impWorld.InventionsDB, impWorld.LocDB);

var traitMapper = new TraitMapper("configurables/trait_map.txt", ModFS);

Expand Down Expand Up @@ -372,266 +372,266 @@
}
}

private void OverwriteCountiesHistory(CountryCollection irCountries, IEnumerable<Governorship> governorships, IList<KeyValuePair<Country, Dependency?>> countyLevelCountries, IEnumerable<Governorship> countyLevelGovernorships, Imperator.Characters.CharacterCollection impCharacters, Imperator.Provinces.ProvinceCollection irProvinces, Date conversionDate) {
Logger.Info("Overwriting counties' history...");
var governorshipsSet = governorships.ToHashSet();
var countyLevelGovernorshipsSet = countyLevelGovernorships.ToHashSet();

foreach (var county in LandedTitles.Where(t => t.Rank == TitleRank.county)) {
if (county.CapitalBaronyProvinceId is null) {
Logger.Warn($"County {county} has no capital barony province!");
continue;
}
ulong capitalBaronyProvinceId = (ulong)county.CapitalBaronyProvinceId;
if (capitalBaronyProvinceId == 0) {
// title's capital province has an invalid ID (0 is not a valid province in CK3)
Logger.Warn($"County {county} has invalid capital barony province!");
continue;
}

if (!Provinces.ContainsKey(capitalBaronyProvinceId)) {
Logger.Warn($"Capital barony province not found: {capitalBaronyProvinceId}");
continue;
}

var ck3CapitalBaronyProvince = Provinces[capitalBaronyProvinceId];
var irProvince = ck3CapitalBaronyProvince.PrimaryImperatorProvince;
if (irProvince is null) { // probably outside of Imperator map
continue;
}

var irCountry = irProvince.OwnerCountry;

if (irCountry is null || irCountry.CountryType == CountryType.rebels) { // e.g. uncolonized Imperator province
county.SetHolder(null, conversionDate);
county.SetDeFactoLiege(null, conversionDate);
} else {
bool given = TryGiveCountyToCountyLevelRuler(county, irCountry);
if (!given) {
given = TryGiveCountyToGovernor(county, irProvince, irCountry);
}
if (!given) {
given = TryGiveCountyToMonarch(county, irCountry);
}
if (!given) {
Logger.Warn($"County {county} was not given to anyone!");
}
}
}
Logger.IncrementProgress();

bool TryGiveCountyToCountyLevelRuler(Title county, Country irCountry) {
var matchingCountyLevelRulers = countyLevelCountries.Where(c => c.Key.Id == irCountry.Id).ToList();
if (matchingCountyLevelRulers.Count == 0) {
return false;
}
var dependency = matchingCountyLevelRulers[0].Value;

// Give county to ruler.
var ck3Ruler = irCountry.Monarch?.CK3Character;
county.ClearHolderSpecificHistory();
var ruleStartDate = irCountry.RulerTerms.OrderBy(t => t.StartDate).Last().StartDate;
county.SetHolder(ck3Ruler, ruleStartDate);
if (dependency is not null) {
var irOverlord = dependency.OverlordId;
var ck3Overlord = irCountries[irOverlord].CK3Title;
county.SetDeFactoLiege(ck3Overlord, dependency.StartDate);
} else {
county.SetDeFactoLiege(null, ruleStartDate);
}
return true;
}

bool TryGiveCountyToMonarch(Title county, Country irCountry) {
var ck3Country = irCountry.CK3Title;
if (ck3Country is null) {
Logger.Warn($"{irCountry.Name} has no CK3 title!"); // should not happen
return false;
}

GiveCountyToMonarch(county, ck3Country);
return true;
}

void GiveCountyToMonarch(Title county, Title ck3Country) {
var date = ck3Country.GetDateOfLastHolderChange();
var holderId = ck3Country.GetHolderId(date);

if (Characters.TryGetValue(holderId, out var holder)) {
county.ClearHolderSpecificHistory();
county.SetHolder(holder, date);
} else {
Logger.Warn($"Holder {holderId} of county {county} doesn't exist!");
}
county.SetDeFactoLiege(null, date);
}

bool TryGiveCountyToGovernor(Title county, Imperator.Provinces.Province impProvince, Country impCountry) {
var ck3Country = impCountry.CK3Title;
if (ck3Country is null) {
Logger.Warn($"{impCountry.Name} has no CK3 title!"); // should not happen
return false;
}
var matchingGovernorships = new List<Governorship>(governorshipsSet.Where(g =>
g.Country.Id == impCountry.Id &&
g.Region.Id == imperatorRegionMapper.GetParentRegionName(impProvince.Id)
));

var ck3CapitalCounty = ck3Country.CapitalCounty;
if (ck3CapitalCounty is null) {
var logLevel = ck3Country.ImperatorCountry?.PlayerCountry == true ? Level.Warn : Level.Debug;
Logger.Log(logLevel, $"{ck3Country} has no capital county!");
return false;
}
// if title belongs to country ruler's capital's de jure duchy, it needs to be directly held by the ruler
var countryCapitalDuchy = ck3CapitalCounty.DeJureLiege;
var deJureDuchyOfCounty = county.DeJureLiege;
if (countryCapitalDuchy is not null && deJureDuchyOfCounty is not null && countryCapitalDuchy.Id == deJureDuchyOfCounty.Id) {
return false;
}

if (matchingGovernorships.Count == 0) {
// we have no matching governorship
return false;
}

// give county to governor
var governorship = matchingGovernorships[0];
var ck3GovernorshipId = tagTitleMapper.GetTitleForGovernorship(governorship, LandedTitles, irProvinces, Provinces, imperatorRegionMapper, provinceMapper);
if (ck3GovernorshipId is null) {
Logger.Warn($"{nameof(ck3GovernorshipId)} is null for {ck3Country} {governorship.Region.Id}!");
return false;
}

if (countyLevelGovernorshipsSet.Contains(governorship)) {
GiveCountyToCountyLevelGovernor(county, governorship, ck3Country);
} else {
GiveCountyToGovernor(county, ck3GovernorshipId);
}
return true;
}

void GiveCountyToGovernor(Title county, string ck3GovernorshipId) {
var ck3Governorship = LandedTitles[ck3GovernorshipId];
var holderChangeDate = ck3Governorship.GetDateOfLastHolderChange();
var holderId = ck3Governorship.GetHolderId(holderChangeDate);
if (Characters.TryGetValue(holderId, out var governor)) {
county.ClearHolderSpecificHistory();
county.SetHolder(governor, holderChangeDate);
} else {
Logger.Warn($"Holder {holderId} of county {county} doesn't exist!");
}
county.SetDeFactoLiege(null, holderChangeDate);
}

void GiveCountyToCountyLevelGovernor(Title county, Governorship governorship, Title ck3Country) {
var holderChangeDate = governorship.StartDate;
var impGovernor = impCharacters[governorship.CharacterId];
var governor = impGovernor.CK3Character;

county.ClearHolderSpecificHistory();
county.SetHolder(governor, holderChangeDate);
county.SetDeFactoLiege(ck3Country, holderChangeDate);
}
}

Check notice on line 537 in ImperatorToCK3/CK3/World.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/World.cs#L375-L537

Complex Method
private void HandleIcelandAndFaroeIslands(Configuration config) {
Logger.Info("Handling Iceland and Faroe Islands...");
Date bookmarkDate = config.CK3BookmarkDate;
var year = bookmarkDate.Year;

var faiths = Religions.Faiths.ToList();
var titleIdsToHandle = new OrderedSet<string> { "d_iceland", "c_faereyar" };

bool generateHermits = true;
IEnumerable<string> faithCandidates = new OrderedSet<string>();
Queue<string> namePool = new();
const string defaultCultureId = "irish";
string cultureId = defaultCultureId;

switch (year) {
case <= 300:
UsePaganRulers();
break;
case < 874:
faithCandidates = new OrderedSet<string> { "insular_celtic", "catholic", "orthodox" };
var christianFaiths = Religions["christianity_religion"].Faiths;

// If there is at least one Irish Christian county, give it to the Irish Papar.
// If there is at least one Christian county of another Gaelic culture, give it to a character of this Gaelic culture.
var cultureCandidates = new[] { "irish", "gaelic" };
bool provinceFound = false;
foreach (var potentialCultureId in cultureCandidates) {
var cultureProvinces = Provinces.Where(p =>
p.GetCultureId(bookmarkDate) == potentialCultureId);
foreach (var cultureProvince in cultureProvinces) {
var faithId = cultureProvince.GetFaithId(bookmarkDate);
if (faithId is null || !christianFaiths.ContainsKey(faithId)) {
continue;
}
provinceFound = true;
cultureId = potentialCultureId;
faithCandidates = faithCandidates.Prepend(faithId);
break;
}
if (provinceFound) {
break;
}
}
if (!provinceFound) {
// If all the Gaels are pagan but at least one province in Ireland or Scotland is Christian,
// give the handled titles to a generated ruler of the same culture as that Christian province.
var potentialSourceProvinces = Provinces.Where(p =>
ck3RegionMapper.ProvinceIsInRegion(p.Id, "custom_ireland") || ck3RegionMapper.ProvinceIsInRegion(p.Id, "custom_scotland"));
foreach (var potentialSourceProvince in potentialSourceProvinces) {
var faithId = potentialSourceProvince.GetFaithId(bookmarkDate);
if (faithId is null || !christianFaiths.ContainsKey(faithId)) {
continue;
}
provinceFound = true;
cultureId = potentialSourceProvince.GetCultureId(bookmarkDate) ?? defaultCultureId;
faithCandidates = faithCandidates.Prepend(faithId);
break;
}
}
if (!provinceFound) {
// Give up and create a pagan ruler.
UsePaganRulers();
} else {
Logger.Info("Giving Iceland and Faroe Islands to Papar...");
namePool = new Queue<string>(["Canann", "Petair", "Fergus"]);
}
break;
default:
Logger.Info("Keeping Iceland and Faroe Islands as is in history...");
// Let CK3 use rulers from its history.
generateHermits = false;
break;
}

if (generateHermits) {
var faithId = faithCandidates.First(c => faiths.Exists(f => f.Id == c));
foreach (var titleId in titleIdsToHandle) {
if (!LandedTitles.TryGetValue(titleId, out var title)) {
Logger.Warn($"Title {titleId} not found!");
continue;
}

GenerateHermitForTitle(title, namePool, bookmarkDate, faithId, cultureId, config);
}
}

Logger.IncrementProgress();

void UsePaganRulers() {
Logger.Info("Giving Iceland and Faroe Islands to pagan Gaels...");
faithCandidates = new OrderedSet<string> { "gaelic_paganism", "celtic_pagan", "briton_paganism", "pagan" };
cultureId = "gaelic";
// ReSharper disable once StringLiteralTypo
namePool = new Queue<string>(new[] { "A_engus", "Domnall", "Rechtabra" });
}
}

Check notice on line 634 in ImperatorToCK3/CK3/World.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/World.cs#L538-L634

Complex Method
private void GenerateHermitForTitle(Title title, Queue<string> namePool, Date bookmarkDate, string faithId, string cultureId, Configuration config) {
Logger.Debug($"Generating hermit for {title.Id}...");

Expand Down Expand Up @@ -780,152 +780,152 @@
}
}

private void GenerateFillerHoldersForUnownedLands(CultureCollection cultures, Configuration config) {
Logger.Info("Generating filler holders for unowned lands...");
var date = config.CK3BookmarkDate;
var unheldCounties = LandedTitles
.Where(c => c.Rank == TitleRank.county && c.GetHolderId(date) == "0")
.ToImmutableList();

var duchyIdToHolderDict = new Dictionary<string, Character>();

foreach (var county in unheldCounties) {
if (config.FillerDukes) {
var duchy = county.DeJureLiege;
if (duchy is not null && duchy.Rank == TitleRank.duchy) {
if (duchyIdToHolderDict.TryGetValue(duchy.Id, out var duchyHolder)) {
county.SetHolder(duchyHolder, date);
continue;
}
}
}

var candidateProvinces = new OrderedSet<Province>();
if (county.CapitalBaronyProvinceId is not null) {
// Give priority to capital province.
if (Provinces.TryGetValue(county.CapitalBaronyProvinceId.Value, out var capitalProvince)) {
candidateProvinces.Add(capitalProvince);
}
}

var allCountyProvinces = county.CountyProvinceIds
.Select(id => Provinces.TryGetValue(id, out var province) ? province : null)
.Where(p => p is not null)
.Select(p => p!);
candidateProvinces.UnionWith(allCountyProvinces);

int pseudoRandomSeed;
if (candidateProvinces.Count != 0) {
pseudoRandomSeed = (int)candidateProvinces.First().Id;
} else {
// Use county ID for seed if no province is available.
pseudoRandomSeed = county.Id.Aggregate(0, (current, c) => current + c);
}

// Determine culture of the holder.
var culture = candidateProvinces
.Select(p => p.GetCulture(date, cultures))
.FirstOrDefault(c => c is not null);
if (culture is null) {
Logger.Debug($"Trying to use de jure duchy for culture of holder for {county.Id}...");
var deJureDuchy = county.DeJureLiege;
if (deJureDuchy is not null) {
culture = Provinces
.Where(p => deJureDuchy.DuchyContainsProvince(p.Id))
.Select(p => p.GetCulture(date, cultures))
.FirstOrDefault(c => c is not null);
}
if (culture is null && deJureDuchy?.DeJureLiege is not null) {
Logger.Debug($"Trying to use de jure kingdom for culture of holder for {county.Id}...");
var deJureKingdom = deJureDuchy.DeJureLiege;
culture = Provinces
.Where(p => deJureKingdom.KingdomContainsProvince(p.Id))
.Select(p => p.GetCulture(date, cultures))
.FirstOrDefault(c => c is not null);
}
if (culture is null) {
Logger.Warn($"Found no fitting culture for generated holder of {county.Id}, " +
$"using first culture from database!");
culture = cultures.First();
}
}

// Determine faith of the holder.
var faithId = candidateProvinces
.Select(p => p.GetFaithId(date))
.FirstOrDefault(f => f is not null);
if (faithId is null) {
Logger.Debug($"Trying to use de jure duchy for faith of holder for {county.Id}...");
var deJureDuchy = county.DeJureLiege;
if (deJureDuchy is not null) {
faithId = Provinces
.Where(p => deJureDuchy.DuchyContainsProvince(p.Id))
.Select(p => p.GetFaithId(date))
.FirstOrDefault(f => f is not null);
}
if (faithId is null && deJureDuchy?.DeJureLiege is not null) {
Logger.Debug($"Trying to use de jure kingdom for faith of holder for {county.Id}...");
var deJureKingdom = deJureDuchy.DeJureLiege;
faithId = Provinces
.Where(p => deJureKingdom.KingdomContainsProvince(p.Id))
.Select(p => p.GetFaithId(date))
.FirstOrDefault(f => f is not null);
}
if (faithId is null) {
Logger.Warn($"Found no fitting faith for generated holder of {county.Id}, " +
$"using first faith from database!");
faithId = Religions.Faiths.First().Id;
}
}

bool female = false;
string name;
var maleNames = culture.MaleNames.ToImmutableList();
if (maleNames.Count > 0) {
name = maleNames.ElementAt(pseudoRandomSeed % maleNames.Count);
} else { // Generate a female if no male name is available.
female = true;
var femaleNames = culture.FemaleNames.ToImmutableList();
name = femaleNames.ElementAt(pseudoRandomSeed % femaleNames.Count);
}
int age = 18 + (pseudoRandomSeed % 60);
var holder = new Character($"IRToCK3_{county.Id}_holder", name, date, Characters) {
Female = female,
BirthDate = date.ChangeByYears(-age)
};
holder.SetFaithId(faithId, null);
holder.SetCultureId(culture.Id, null);
holder.History.AddFieldValue(date, "government", "change_government", "tribal_government");
Characters.AddOrReplace(holder);

county.SetHolder(holder, date);
if (config.FillerDukes) {
var duchy = county.DeJureLiege;
if (duchy is null || duchy.Rank != TitleRank.duchy) {
continue;
}

duchy.SetHolder(holder, date);
duchy.SetGovernment("tribal_government", date);
duchyIdToHolderDict[duchy.Id] = holder;
} else {
county.SetGovernment("tribal_government", date);
}
}
}

private readonly CoaMapper coaMapper;
private readonly DeathReasonMapper deathReasonMapper = new();
private readonly DefiniteFormMapper definiteFormMapper = new(Path.Combine("configurables", "definite_form_names.txt"));
private readonly NicknameMapper nicknameMapper = new(Path.Combine("configurables", "nickname_map.txt"));
private readonly ProvinceMapper provinceMapper = new();
private readonly SuccessionLawMapper successionLawMapper = new(Path.Combine("configurables", "succession_law_map.txt"));
private readonly TagTitleMapper tagTitleMapper = new(
tagTitleMappingsPath: Path.Combine("configurables", "title_map.txt"),
governorshipTitleMappingsPath: Path.Combine("configurables", "governorMappings.txt"),
rankMappingsPath: "configurables/country_rank_map.txt"
);
private readonly UnitTypeMapper unitTypeMapper = new("configurables/unit_types_map.txt");
private readonly CK3RegionMapper ck3RegionMapper;
private readonly ImperatorRegionMapper imperatorRegionMapper;
private readonly WarMapper warMapper = new("configurables/wargoal_mappings.txt");

Check notice on line 931 in ImperatorToCK3/CK3/World.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/World.cs#L783-L931

Complex Method
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ bonus = { ir = gallic_chainmail_inv ck3 = innovation_quilted_armor }
# TRIBAL ERA - CIVIC
bonus = { ir = global_citizen_output_inv_3 ir = global_freemen_output_inv_3 ir = italic_lex_frumentaria_inv ir = global_tax_modifier_inv_3 ck3 = innovation_development_01 }
link = { ir = ruler_popularity_gain_inv_4 ck3 = innovation_development_01 } # Public Works
# bonus = { ir = ck3 = innovation_currency_01 }
bonus = { ir = commerce_inv_1 ir = global_tax_modifier_inv_1 ir = commerce_inv_2 ck3 = innovation_currency_01 }
# link = { ir = ck3 = innovation_currency_01 }
# bonus = { ir = ck3 = innovation_gavelkind }
link = { ir = oaths_of_allegiance_inv ck3 =innovation_gavelkind } # IR is Codified Succession
Expand Down Expand Up @@ -90,7 +90,8 @@ link = { ir = global_settlement_building_slot_inv ck3 = innovation_manorialism }
bonus = { ir = smear_character_cost_modifier_inv ck3 = innovation_development_02 } # 1st Town Criers
# link = { ir = ck3 = innovation_development_02 } # Communal Government
bonus = { ir = hold_triumph_cost_modifier_inv ck3 = innovation_currency_02 }
# link = { ir = ck3 = innovation_currency_02 } # Coinage
link = { ir = coins_inv ck3 = innovation_currency_02 } # Coinage
link = { ir = brass_coinage_inv ck3 = innovation_currency_02 } # Coinage
bonus = { ir = loyalty_gain_chance_modifier_inv ir = agressive_expansion_monthly_decay_inv_4 ck3 = innovation_royal_prerogative }
# link = { ir = ck3 = innovation_royal_prerogative }
bonus = { ir = land_won_by_the_spear ir = religious_tech_investment_inv_1 ck3 = innovation_chronicle_writing } # 2nd Recording Tradition
Expand Down
18 changes: 15 additions & 3 deletions ImperatorToCK3/Mappers/Technology/InnovationMapper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using commonItems;
using commonItems.Localization;
using ImperatorToCK3.Imperator.Inventions;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -50,12 +51,23 @@ public IDictionary<string, ushort> GetInnovationProgresses(ICollection<string> i
return progressesToReturn;
}

public void LogUnmappedInventions(InventionsDB inventionsDB) {
public void LogUnmappedInventions(InventionsDB inventionsDB, LocDB irLocDB) {
// Log Imperator inventions for which neither link nor bonus for CK3 innovations exists.
var unmappedInventions = inventionsDB.InventionIds
.Where(invention => !innovationLinks.Exists(link => link.Match(invention) is not null))
.Where(invention => !innovationBonuses.Exists(bonus => bonus.GetProgress([invention]) is not null));
.Where(invention => !innovationBonuses.Exists(bonus => bonus.GetProgress([invention]) is not null))
.ToList();

Logger.Debug($"Unmapped I:R inventions: {string.Join(", ", unmappedInventions)}");
var inventionsWithLoc = unmappedInventions
.Select(inventionId => {
if (irLocDB.GetLocBlockForKey(inventionId) is { } locBlock) {
return $"{inventionId} ({locBlock[ConverterGlobals.PrimaryLanguage]})";
}
return inventionId;
});

Logger.Debug($"Unmapped I:R inventions: {string.Join(", ", inventionsWithLoc)}");
}

// TODO: ALSO LOG UNMAPPED CK3 MARTIAL AND CIVIC INNOVATIONS
}
Loading