-
-
Notifications
You must be signed in to change notification settings - Fork 22
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
base: master
Are you sure you want to change the base?
Changes from all commits
2a9c5de
8a94f8c
cbb0a7c
9df6d97
afc0b03
dbd0191
7560bec
df01650
21ec47b
dd5918b
e620a07
12efe22
355f41d
f2b309b
752e40a
2c9ec4c
f5d6e69
87643a3
ba2e6bd
a48fcc7
5e723f5
6428179
d93481c
a87e690
27a2f22
4b7e56e
dc475f7
66d459b
ef63015
47d3a7b
963f956
1f93e77
ae90d51
0927725
2e83362
c57b830
e948774
d09a420
81a5e71
b8d8a98
eddb1f3
1d65d8a
0a49785
eee2bcd
68ce469
c8fac68
ad20a9d
4e49056
5890daa
e251464
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -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 GitHub Actions / build (macos-14)
Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / test (macos-14)
Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / build (self-hosted, linux)
Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / test_and_check_coverage
Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / Upload development build (linux-x64)
Check warning on line 723 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / Upload development build (win-x64)
|
||
|
||
// TODO: check if needed: Create visuals for artifacts. | ||
Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / build (macos-14)
Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / test (macos-14)
Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / build (self-hosted, linux)
Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / test_and_check_coverage
Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / Upload development build (linux-x64)
Check warning on line 725 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / Upload development build (win-x64)
|
||
/*var treasureIconNames = treasureManager | ||
.Select(t => t.IconName) | ||
.Distinct() | ||
.ToList(); | ||
foreach (var iconName in treasureIconNames) { | ||
throw new NotImplementedException(); | ||
// example: | ||
// icon: artefact_icons_unique_artifact_cheese | ||
// 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 GitHub Actions / build (macos-14)
Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / test (macos-14)
Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / build (self-hosted, linux)
Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / test_and_check_coverage
Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / Upload development build (linux-x64)
Check warning on line 746 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs GitHub Actions / Upload development build (win-x64)
|
||
foreach (var irArtifactId in irArtifactIds) { | ||
var irArtifact = treasureManager[irArtifactId]; | ||
ImportArtifact(character, irArtifact, modifierMapper, ck3Modifiers, visualsMapper, irLocDB, ck3LocDB, date); | ||
} | ||
|
||
|
||
|
||
/* | ||
* # Create the artifact | ||
create_artifact = { | ||
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; | ||
}); | ||
} | ||
|
||
internal void ConvertImperatorCharacterDNA(DNAFactory dnaFactory) { | ||
Logger.Info("Converting Imperator character DNA to CK3..."); | ||
foreach (var character in this) { | ||
|
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(); | ||
} | ||
} |
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(); | ||
} | ||
} |
There was a problem hiding this comment.
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"