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

[WIP] Conversion of treasures into artifacts #2474

Draft
wants to merge 50 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
2a9c5de
Treasure and TreasureManager classes
IhateTrains Mar 26, 2023
8a94f8c
Load treasures from save
IhateTrains Mar 26, 2023
cbb0a7c
Merge branch 'master' into treasures
IhateTrains Mar 27, 2023
9df6d97
Begin work on ImportArtifacts function
IhateTrains Mar 27, 2023
afc0b03
TODO
IhateTrains Mar 27, 2023
dbd0191
Merge branch 'master' into treasures
IhateTrains Mar 28, 2023
7560bec
Merge branch 'master' into treasures
IhateTrains Mar 28, 2023
df01650
Merge branch 'master' into treasures
IhateTrains Mar 30, 2023
21ec47b
Merge branch 'master' into treasures
IhateTrains Apr 1, 2023
dd5918b
ImportArtifact base
IhateTrains Apr 1, 2023
e620a07
Rename HolySiteEffectMapper to ModifierMapper
IhateTrains Apr 1, 2023
12efe22
ModifierCollection for CK3 modifiers
IhateTrains Apr 2, 2023
355f41d
Update CharacterCollection.cs
IhateTrains Apr 4, 2023
f2b309b
Update CharacterCollection.cs
IhateTrains Apr 4, 2023
752e40a
Merge branch 'master' into treasures
IhateTrains Apr 10, 2023
2c9ec4c
Merge branch 'master' into treasures
IhateTrains Apr 20, 2023
f5d6e69
Merge branch 'master' into treasures
IhateTrains May 27, 2023
87643a3
Merge branch 'master' into treasures
IhateTrains Jun 10, 2023
ba2e6bd
Merge branch 'master' into treasures
IhateTrains Jun 11, 2023
a48fcc7
Merge branch 'master' into treasures
IhateTrains Jun 17, 2023
5e723f5
Merge branch 'master' into treasures
IhateTrains Jun 26, 2023
6428179
Merge branch 'master' into treasures
IhateTrains Jul 1, 2023
d93481c
Merge branch 'master' into treasures
IhateTrains Jul 15, 2023
a87e690
Merge branch 'master' into treasures
IhateTrains Jul 17, 2023
27a2f22
Merge branch 'master' into treasures
IhateTrains Jul 18, 2023
4b7e56e
Merge branch 'master' into treasures
IhateTrains Feb 23, 2024
dc475f7
Merge branch 'master' into treasures
IhateTrains Mar 14, 2024
66d459b
Merge branch 'master' into treasures
IhateTrains Mar 22, 2024
ef63015
Merge branch 'master' into treasures
IhateTrains Apr 14, 2024
47d3a7b
Merge branch 'master' into treasures
IhateTrains Sep 16, 2024
963f956
Merge branch 'master' into treasures
IhateTrains Sep 23, 2024
1f93e77
Merge branch 'master' into treasures
IhateTrains Sep 24, 2024
ae90d51
Merge branch 'master' into treasures
IhateTrains Sep 25, 2024
0927725
Merge branch 'master' into treasures
IhateTrains Sep 26, 2024
2e83362
Merge branch 'master' into treasures
IhateTrains Sep 27, 2024
c57b830
Post-merge fixes
IhateTrains Sep 27, 2024
e948774
Merge branch 'master' into treasures
IhateTrains Oct 21, 2024
d09a420
Merge branch 'master' into treasures
IhateTrains Oct 26, 2024
81a5e71
Post-merge fix
IhateTrains Oct 26, 2024
b8d8a98
Implement mappings loading in ArtifactVisualsMapper
IhateTrains Oct 26, 2024
eddb1f3
ArtifactVisualsMapper.GetVisual
IhateTrains Oct 28, 2024
1d65d8a
Merge branch 'master' into treasures
IhateTrains Jan 24, 2025
0a49785
Post-merge fix
IhateTrains Jan 24, 2025
eee2bcd
Use ArtifactVisualsMapper in ImportArtifact
IhateTrains Jan 24, 2025
68ce469
Add ck3Type to artifact mappings
IhateTrains Jan 26, 2025
c8fac68
Merge branch 'master' into treasures
IhateTrains Jan 26, 2025
ad20a9d
Exception fix and cleanup
IhateTrains Jan 26, 2025
4e49056
Add mapping for cr_treasure_skins_of_the_gorilla_03 treasure icon
IhateTrains Jan 26, 2025
5890daa
Update explanation in artifact_map.txt
IhateTrains Jan 26, 2025
e251464
Merge branch 'master' into treasures
IhateTrains Jan 28, 2025
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
4 changes: 2 additions & 2 deletions ImperatorToCK3.UnitTests/CK3/Religions/HolySiteTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using commonItems.Serialization;
using ImperatorToCK3.CK3.Religions;
using ImperatorToCK3.CK3.Titles;
using ImperatorToCK3.Mappers.HolySiteEffect;
using ImperatorToCK3.Mappers.Modifier;
using System.Collections.Generic;
using Xunit;

Expand Down Expand Up @@ -58,7 +58,7 @@ public void HolySiteCanBeConstructedForBaronyAndFaithWithEffects() {
var titlesReader = new BufferedReader("c_county = { b_barony = { province = 1 } }");
titles.LoadTitles(titlesReader);

var holySiteEffectMapper = new HolySiteEffectMapper("TestFiles/configurables/holy_site_effect_mappings.txt");
var holySiteEffectMapper = new ModifierMapper("TestFiles/configurables/holy_site_effect_mappings.txt");
var imperatorEffects = new OrderedDictionary<string, double> {
{"discipline", 0.2f}, // will be converted to knight_effectiveness_mult with factor of 10
{"unmapped_effect", 1f}, // will be skipped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using ImperatorToCK3.CK3.Religions;
using ImperatorToCK3.CK3.Titles;
using ImperatorToCK3.Imperator.Pops;
using ImperatorToCK3.Mappers.HolySiteEffect;
using ImperatorToCK3.Mappers.Modifier;
using System;
using System.Linq;
using Xunit;
Expand Down Expand Up @@ -155,7 +155,7 @@ Province GenerateCK3AndImperatorProvinceWithPops(ulong provId, int popCount, boo
religions.DetermineHolySites(
provinces,
imperatorReligions,
new HolySiteEffectMapper("TestFiles/HolySiteEffectMapperTests/mappings.txt"),
new ModifierMapper("TestFiles/HolySiteEffectMapperTests/mappings.txt"),
new Date("476.1.1")
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using ImperatorToCK3.Mappers.HolySiteEffect;
using ImperatorToCK3.Mappers.Modifier;
using System.Collections.Generic;
using Xunit;

Expand All @@ -8,7 +8,7 @@ public class HolySiteEffectMapperTests {
[Theory, MemberData(nameof(TestData))]
public void MapperReturnsCorrectValues(string imperatorEffect, double imperatorValue, KeyValuePair<string, double>? match) {
const string mappingsFilePath = "TestFiles/HolySiteEffectMapperTests/mappings.txt";
var mapper = new HolySiteEffectMapper(mappingsFilePath);
var mapper = new ModifierMapper(mappingsFilePath);
Assert.Equal(match, mapper.Match(imperatorEffect, imperatorValue));
}

Expand Down
194 changes: 194 additions & 0 deletions ImperatorToCK3/CK3/Characters/CharacterCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@
using commonItems.Collections;
using commonItems.Localization;
using ImperatorToCK3.CK3.Armies;
using ImperatorToCK3.CK3.Modifiers;
using ImperatorToCK3.CK3.Cultures;
using ImperatorToCK3.CK3.Dynasties;
using ImperatorToCK3.CK3.Titles;
using ImperatorToCK3.CommonUtils;
using ImperatorToCK3.CommonUtils.Map;
using ImperatorToCK3.Imperator.Armies;
using ImperatorToCK3.Imperator.Provinces;
using ImperatorToCK3.Imperator.Religions;
using ImperatorToCK3.Imperator.Characters;
using ImperatorToCK3.Mappers.Artifact;
using ImperatorToCK3.Mappers.Culture;
using ImperatorToCK3.Mappers.DeathReason;
using ImperatorToCK3.Mappers.Modifier;
using ImperatorToCK3.Mappers.Nickname;
using ImperatorToCK3.Mappers.Province;
using ImperatorToCK3.Mappers.Religion;
Expand Down Expand Up @@ -661,156 +666,345 @@
Logger.IncrementProgress();
}

public void ImportArtifacts(
ProvinceCollection irProvinces,
ProvinceMapper provinceMapper,
Title.LandedTitles titles,
TreasureManager treasureManager,
ModifierMapper modifierMapper,
ModifierCollection ck3Modifiers,
LocDB irLocDB,
CK3LocDB ck3LocDB,
Date date
) {
Logger.Info("Importing Imperator artifacts...");
var ck3CharacterIdToTreasureIdsListDict = new Dictionary<string, IList<ulong>>();

foreach (var irProvince in irProvinces) {
var ck3Provinces = provinceMapper.GetCK3ProvinceNumbers(irProvince.Id);
if (ck3Provinces.Count == 0) {
continue;
}
var primaryCK3ProvinceId = ck3Provinces.First();

string? ownerId = null;

var barony = titles.GetBaronyForProvince(primaryCK3ProvinceId);
if (barony is null) {
Logger.Warn($"Can't find barony for province {primaryCK3ProvinceId}!");
continue;
}
var baronyHolderId = barony.GetHolderId(date);
if (baronyHolderId != "0") {
ownerId = baronyHolderId;
} else {
var county = titles.GetCountyForProvince(primaryCK3ProvinceId);
if (county is null) {
Logger.Warn($"Can't find county for province {primaryCK3ProvinceId}!");
continue;
}
var countyHolderId = county.GetHolderId(date);
if (countyHolderId != "0") {
ownerId = countyHolderId;
}
}

if (ownerId is null) {
Logger.Warn($"Can't find owner for province {primaryCK3ProvinceId}!");
continue;
}

if (ck3CharacterIdToTreasureIdsListDict.TryGetValue(ownerId, out var artifactList)) {
artifactList.AddRange(irProvince.TreasureIds);
} else {
ck3CharacterIdToTreasureIdsListDict[ownerId] = irProvince.TreasureIds.ToList();
}
}
// TODO: also import artifacts not assigned to holy sites (search for treasures={ 225 226 } in save)

Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / build (macos-14)

TODO also import artifacts not assigned to holy sites (search for treasures={ 225 226 } in save) (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / test (macos-14)

TODO also import artifacts not assigned to holy sites (search for treasures={ 225 226 } in save) (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / build (self-hosted, linux)

TODO also import artifacts not assigned to holy sites (search for treasures={ 225 226 } in save) (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / test_and_check_coverage

TODO also import artifacts not assigned to holy sites (search for treasures={ 225 226 } in save) (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / Upload development build (linux-x64)

TODO also import artifacts not assigned to holy sites (search for treasures={ 225 226 } in save) (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / Upload development build (win-x64)

TODO also import artifacts not assigned to holy sites (search for treasures={ 225 226 } in save) (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

// TODO: check if needed: Create visuals for artifacts.

Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / build (macos-14)

TODO check if needed: Create visuals for artifacts. (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / test (macos-14)

TODO check if needed: Create visuals for artifacts. (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / build (self-hosted, linux)

TODO check if needed: Create visuals for artifacts. (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / test_and_check_coverage

TODO check if needed: Create visuals for artifacts. (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / Upload development build (linux-x64)

TODO check if needed: Create visuals for artifacts. (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / Upload development build (win-x64)

TODO check if needed: Create visuals for artifacts. (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)
/*var treasureIconNames = treasureManager
.Select(t => t.IconName)
.Distinct()
.ToList();

Check warning on line 729 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

ImperatorToCK3/CK3/Characters/CharacterCollection.cs#L729

Remove this commented out code.
foreach (var iconName in treasureIconNames) {
throw new NotImplementedException();
// example:
// icon: artefact_icons_unique_artifact_cheese

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[misspell] reported by reviewdog 🐶
"artefact" is a misspelling of "artifact"

// asset entity: ep1_western_pouch_basic_01_a_entity
// mesh: ep1_western_pouch_basic_01_a_mesh
}*/

var visualsMapper = new ArtifactMapper("configurables/artifact_map.txt");

var charactersFromImperator = this.Where(c => c.FromImperator).ToList();
foreach (var character in charactersFromImperator) {
if (!ck3CharacterIdToTreasureIdsListDict.TryGetValue(character.Id, out var irArtifactIds)) {
continue;
}

// TODO: try to use create_artifact_sculpture_babr_e_bayan_effect as base

Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / build (macos-14)

TODO try to use create_artifact_sculpture_babr_e_bayan_effect as base (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / test (macos-14)

TODO try to use create_artifact_sculpture_babr_e_bayan_effect as base (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / build (self-hosted, linux)

TODO try to use create_artifact_sculpture_babr_e_bayan_effect as base (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / test_and_check_coverage

TODO try to use create_artifact_sculpture_babr_e_bayan_effect as base (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / Upload development build (linux-x64)

TODO try to use create_artifact_sculpture_babr_e_bayan_effect as base (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)

Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View workflow job for this annotation

GitHub Actions / Upload development build (win-x64)

TODO try to use create_artifact_sculpture_babr_e_bayan_effect as base (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md)
foreach (var irArtifactId in irArtifactIds) {
var irArtifact = treasureManager[irArtifactId];
ImportArtifact(character, irArtifact, modifierMapper, ck3Modifiers, visualsMapper, irLocDB, ck3LocDB, date);
}



/*
* # Create the artifact
create_artifact = {

Check warning on line 756 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

ImperatorToCK3/CK3/Characters/CharacterCollection.cs#L756

Remove this commented out code.
name = artifact_sculpture_armor_babr_name
description = artifact_sculpture_armor_babr
type = sculpture
template = babr_template
visuals = sculpture_babr_e_bayan
wealth = scope:wealth
quality = scope:quality
history = {
type = created_before_history
}
modifier = babr_e_bayan_modifier
save_scope_as = newly_created_artifact
decaying = yes
}

scope:newly_created_artifact = {
set_variable = { name = historical_unique_artifact value = yes }
set_variable = babr_e_bayan
save_scope_as = epic
}
*/
}

Logger.IncrementProgress();
}

private void ImportArtifact(Character character, Treasure irArtifact, ModifierMapper modifierMapper, ModifierCollection ck3Modifiers, ArtifactMapper artifactMapper, LocDB irLocDB, CK3LocDB ck3LocDB, Date date) {
var visualAndType = artifactMapper.GetVisualAndType(irArtifact.Key, irArtifact.IconName);
if (visualAndType is null) {
Logger.Warn($"Can't find a match for I:R artifact key {irArtifact.Key} and icon {irArtifact.IconName}!");
return;
}
var (ck3Visual, ck3Type) = visualAndType.Value;

var ck3ArtifactName = $"IRToCK3_artifact_{irArtifact.Key}_{irArtifact.Id}";
var irNameLoc = irLocDB.GetLocBlockForKey(irArtifact.Key);
if (irNameLoc is null) {
Logger.Warn($"Can't find name loc for artifact {irArtifact.Key}!");
} else {
var artifactNameLocBlock = ck3LocDB.GetOrCreateLocBlock(ck3ArtifactName);
artifactNameLocBlock.CopyFrom(irNameLoc);
}

var ck3DescKey = $"{ck3ArtifactName}_desc";
var irDescLoc = irLocDB.GetLocBlockForKey(irArtifact.Key + "_desc");
if (irDescLoc is null) {
Logger.Warn($"Can't find description loc for artifact {irArtifact.Key}!");
} else {
var descLocBlock = ck3LocDB.GetOrCreateLocBlock(ck3DescKey);
descLocBlock.CopyFrom(irDescLoc);
}

var artifactScope = $"newly_created_artifact_{irArtifact.Id}";

var ck3ModifierEffects = new Dictionary<string, double>();
foreach (var (irEffect, irEffectValue) in irArtifact.StateModifiers) {
var match = modifierMapper.Match(irEffect, irEffectValue);
if (match is null) {
Logger.Warn($"Can't find CK3 modifier for Imperator modifier {irEffect}!");
continue;
}
ck3ModifierEffects[match.Value.Key] = match.Value.Value;
}
foreach (var (irEffect, irEffectValue) in irArtifact.CharacterModifiers) {
var match = modifierMapper.Match(irEffect, irEffectValue);
if (match is null) {
Logger.Warn($"Can't find CK3 modifier for Imperator modifier {irEffect}!");
continue;
}
ck3ModifierEffects[match.Value.Key] = match.Value.Value;
}
var ck3ModifierId = $"{ck3ArtifactName}_modifier";
ck3Modifiers.Add(new Modifier(ck3ModifierId, ck3ModifierEffects));

string createArtifactEffect = $$"""
set_artifact_rarity_illustrious = yes
create_artifact = {
name = {{ ck3ArtifactName }}
description = {{ ck3DescKey }}
type = {{ ck3Type }}
# template = babr_template # TODO: check if needed
visuals = {{ ck3Visual }}
wealth = scope:wealth
quality = scope:quality
history = {
type = created_before_history
}
modifier = {{ ck3ModifierId }}
save_scope_as = {{ artifactScope }}
decaying = yes
}
scope:{{ artifactScope }} = {
set_variable = {
name = historical_unique_artifact
value = yes
}
}
""";
character.History.AddFieldValue(date, "effects", "effect", createArtifactEffect);
}

public void GenerateSuccessorsForOldCharacters(Title.LandedTitles titles, CultureCollection cultures, Date irSaveDate, Date ck3BookmarkDate, ulong randomSeed) {
Logger.Info("Generating successors for old characters...");

var oldCharacters = this
.Where(c => c.BirthDate < ck3BookmarkDate && c.DeathDate is null)
.Where(c => ck3BookmarkDate.DiffInYears(c.BirthDate) > 60)
.ToArray();

var titleHolderIds = titles.GetHolderIdsForAllTitlesExceptNobleFamilyTitles(ck3BookmarkDate);

var oldTitleHolders = oldCharacters
.Where(c => titleHolderIds.Contains(c.Id))
.ToArray();

// For characters that don't hold any titles, just set up a death date.
var randomForCharactersWithoutTitles = new Random((int)randomSeed);
foreach (var oldCharacter in oldCharacters.Except(oldTitleHolders)) {
// Roll a dice to determine how much longer the character will live.
var yearsToLive = randomForCharactersWithoutTitles.Next(0, 30);

// If the character is female and pregnant, make sure she doesn't die before the pregnancy ends.
if (oldCharacter is {Female: true, ImperatorCharacter: not null}) {
var lastPregnancy = oldCharacter.Pregnancies.OrderBy(p => p.BirthDate).LastOrDefault();
if (lastPregnancy is not null) {
oldCharacter.DeathDate = lastPregnancy.BirthDate.ChangeByYears(yearsToLive);
continue;
}
}

oldCharacter.DeathDate = irSaveDate.ChangeByYears(yearsToLive);
}

ConcurrentDictionary<string, Title[]> titlesByHolderId = new(titles
.Select(t => new {Title = t, HolderId = t.GetHolderId(ck3BookmarkDate)})
.Where(t => t.HolderId != "0")
.GroupBy(t => t.HolderId)
.ToDictionary(g => g.Key, g => g.Select(t => t.Title).ToArray()));

ConcurrentDictionary<string, string[]> cultureIdToMaleNames = new(cultures
.ToDictionary(c => c.Id, c => c.MaleNames.ToArray()));

// For title holders, generate successors and add them to title history.
Parallel.ForEach(oldTitleHolders, oldCharacter => {
// Get all titles held by the character.
var heldTitles = titlesByHolderId[oldCharacter.Id];
string? dynastyId = oldCharacter.GetDynastyId(ck3BookmarkDate);
string? dynastyHouseId = oldCharacter.GetDynastyHouseId(ck3BookmarkDate);
string? faithId = oldCharacter.GetFaithId(ck3BookmarkDate);
string? cultureId = oldCharacter.GetCultureId(ck3BookmarkDate);
string[] maleNames;
if (cultureId is not null) {
maleNames = cultureIdToMaleNames[cultureId];
} else {
Logger.Warn($"Failed to find male names for successors of {oldCharacter.Id}.");
maleNames = ["Alexander"];
}

var randomSeedForCharacter = randomSeed ^ (oldCharacter.ImperatorCharacter?.Id ?? 0);
var random = new Random((int)randomSeedForCharacter);

int successorCount = 0;
Character currentCharacter = oldCharacter;
Date currentCharacterBirthDate = currentCharacter.BirthDate;
while (ck3BookmarkDate.DiffInYears(currentCharacterBirthDate) >= 90) {
// If the character has living male children, the oldest one will be the successor.
var successorAndBirthDate = currentCharacter.Children
.Where(c => c is {Female: false, DeathDate: null})
.Select(c => new { Character = c, c.BirthDate })
.OrderBy(x => x.BirthDate)
.FirstOrDefault();

Character successor;
Date currentCharacterDeathDate;
Date successorBirthDate;
if (successorAndBirthDate is not null) {
successor = successorAndBirthDate.Character;
successorBirthDate = successorAndBirthDate.BirthDate;

// Roll a dice to determine how much longer the character will live.
// But make sure the successor is at least 16 years old when the old character dies.
var successorAgeAtBookmarkDate = ck3BookmarkDate.DiffInYears(successorBirthDate);
var yearsUntilSuccessorBecomesAnAdult = Math.Max(16 - successorAgeAtBookmarkDate, 0);

var yearsToLive = random.Next((int)Math.Ceiling(yearsUntilSuccessorBecomesAnAdult), 25);
int currentCharacterAge = random.Next(30 + yearsToLive, 80);
currentCharacterDeathDate = currentCharacterBirthDate.ChangeByYears(currentCharacterAge);
// Needs to be after the save date.
if (currentCharacterDeathDate <= irSaveDate) {
currentCharacterDeathDate = irSaveDate.ChangeByDays(1);
}
} else {
// We don't want all the generated successors on the map to have the same birth date.
var yearsUntilHeir = random.Next(1, 5);

// Make the old character live until the heir is at least 16 years old.
var successorAge = random.Next(yearsUntilHeir + 16, 30);
int currentCharacterAge = random.Next(30 + successorAge, 80);
currentCharacterDeathDate = currentCharacterBirthDate.ChangeByYears(currentCharacterAge);
if (currentCharacterDeathDate <= irSaveDate) {
currentCharacterDeathDate = irSaveDate.ChangeByDays(1);
}

// Generate a new successor.
string id = $"irtock3_{oldCharacter.Id}_successor_{successorCount}";
string firstName = maleNames[random.Next(0, maleNames.Length)];

successorBirthDate = currentCharacterDeathDate.ChangeByYears(-successorAge);
successor = new Character(id, firstName, successorBirthDate, this) {FromImperator = true};
Add(successor);
if (currentCharacter.Female) {
successor.Mother = currentCharacter;
} else {
successor.Father = currentCharacter;
}
if (cultureId is not null) {
successor.SetCultureId(cultureId, null);
}
if (faithId is not null) {
successor.SetFaithId(faithId, null);
}
if (dynastyId is not null) {
successor.SetDynastyId(dynastyId, null);
}
if (dynastyHouseId is not null) {
successor.SetDynastyHouseId(dynastyHouseId, null);
}
}

currentCharacter.DeathDate = currentCharacterDeathDate;
// On the old character death date, the successor should inherit all titles.
foreach (var heldTitle in heldTitles) {
heldTitle.SetHolder(successor, currentCharacterDeathDate);
}

// Move to the successor and repeat the process.
currentCharacter = successor;
currentCharacterBirthDate = successorBirthDate;
++successorCount;
}

// After the loop, currentCharacter should represent the successor at bookmark date.
// Set his DNA to avoid weird looking character on the bookmark screen in CK3.
currentCharacter.DNA = oldCharacter.DNA;

// Transfer gold to the living successor.
currentCharacter.Gold = oldCharacter.Gold;
oldCharacter.Gold = null;
});
}

Check notice on line 1007 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/Characters/CharacterCollection.cs#L858-L1007

Complex Method
internal void ConvertImperatorCharacterDNA(DNAFactory dnaFactory) {
Logger.Info("Converting Imperator character DNA to CK3...");
foreach (var character in this) {
Expand Down
25 changes: 25 additions & 0 deletions ImperatorToCK3/CK3/Modifiers/Modifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using commonItems.Collections;
using System.Collections.Generic;
using System.Text;

namespace ImperatorToCK3.CK3.Modifiers;

public class Modifier : IIdentifiable<string> {
public string Id { get; }
private readonly Dictionary<string, double> effects = new();

public Modifier(string id, IDictionary<string, double> effects) {
Id = id;
this.effects = new Dictionary<string, double>(effects);
}

public override string ToString() {
var output = new StringBuilder();
output.AppendLine($"{Id} = {{");
foreach (var effect in effects) {
output.AppendLine($"\t{effect.Key} = {effect.Value}");
}
output.AppendLine("}");
return output.ToString();
}
}
14 changes: 14 additions & 0 deletions ImperatorToCK3/CK3/Modifiers/ModifierCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using commonItems.Collections;
using System.Text;

namespace ImperatorToCK3.CK3.Modifiers;

public class ModifierCollection : IdObjectCollection<string, Modifier> {
public override string ToString() {
var output = new StringBuilder();
foreach (var modifier in this) {
output.AppendLine(modifier.ToString());
}
return output.ToString();
}
}
6 changes: 3 additions & 3 deletions ImperatorToCK3/CK3/Religions/HolySite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using commonItems.Serialization;
using commonItems.SourceGenerators;
using ImperatorToCK3.CK3.Titles;
using ImperatorToCK3.Mappers.HolySiteEffect;
using ImperatorToCK3.Mappers.Modifier;
using System.Collections.Generic;

namespace ImperatorToCK3.CK3.Religions;
Expand Down Expand Up @@ -82,10 +82,10 @@ public HolySite(
Faith faith,
Title.LandedTitles titles,
OrderedDictionary<string, double> imperatorEffects,
HolySiteEffectMapper holySiteEffectMapper
ModifierMapper modifierMapper
) : this(barony, faith, titles) {
foreach (var (effect, value) in imperatorEffects) {
var ck3EffectOpt = holySiteEffectMapper.Match(effect, value);
var ck3EffectOpt = modifierMapper.Match(effect, value);
if (ck3EffectOpt is not { } ck3Effect) {
continue;
}
Expand Down
12 changes: 6 additions & 6 deletions ImperatorToCK3/CK3/Religions/ReligionCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using ImperatorToCK3.CK3.Cultures;
using ImperatorToCK3.CK3.Titles;
using ImperatorToCK3.CK3.Provinces;
using ImperatorToCK3.Mappers.HolySiteEffect;
using ImperatorToCK3.Mappers.Modifier;
using Open.Collections;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -201,7 +201,7 @@ private HolySite GenerateHolySiteForBarony(
Faith ck3Faith,
ProvinceCollection ck3Provinces,
Imperator.Religions.ReligionCollection imperatorReligions,
HolySiteEffectMapper holySiteEffectMapper
ModifierMapper modifierMapper
) {
var imperatorProvince = GetImperatorProvinceForBarony(barony, ck3Provinces);
if (imperatorProvince is null) {
Expand All @@ -223,13 +223,13 @@ HolySiteEffectMapper holySiteEffectMapper
}
}

return new HolySite(barony, ck3Faith, landedTitles, imperatorModifiers, holySiteEffectMapper);
return new HolySite(barony, ck3Faith, landedTitles, imperatorModifiers, modifierMapper);
}

public void DetermineHolySites(
ProvinceCollection ck3Provinces,
Imperator.Religions.ReligionCollection imperatorReligions,
HolySiteEffectMapper holySiteEffectMapper,
ModifierMapper modifierMapper,
Date date
) {
var provincesByFaith = GetProvincesFromImperatorByFaith(ck3Provinces, date);
Expand Down Expand Up @@ -258,7 +258,7 @@ Date date
faith,
ck3Provinces,
imperatorReligions,
holySiteEffectMapper
modifierMapper
);
if (HolySites.ContainsKey(newHolySiteInSameBarony.Id)) {
Logger.Warn($"Created duplicate holy site: {newHolySiteInSameBarony.Id}!");
Expand All @@ -279,7 +279,7 @@ Date date
faith,
ck3Provinces,
imperatorReligions,
holySiteEffectMapper
modifierMapper
);
if (HolySites.ContainsKey(replacementSite.Id)) {
Logger.Warn($"Created duplicate holy site: {replacementSite.Id}!");
Expand Down
Loading
Loading