public static bool Prefix(CharacterInspectionScreen __instance, InputCommands.Id command) { switch (command) { // Handle 'E' for 'Export' case InputCommands.Id.RotateCCW: SaveCharacter(); break; } return(true); void SaveCharacter() { var rulesetEntityService = ServiceRepository.GetService <IRulesetEntityService>(); using (var logger = new MethodLogger("CharacterInspectionScreen_HandleInput")) { var heroCharacter = __instance.InspectedCharacter.RulesetCharacterHero; // record current name, etc.. var name = heroCharacter.Name; var builtin = heroCharacter.BuiltIn; var guid = heroCharacter.Guid; // record current conditions, powers, spells and attunements var conditions = heroCharacter.ConditionsByCategory.ToList(); var powers = heroCharacter.PowersUsedByMe.ToList(); var spells = heroCharacter.SpellsCastByMe.ToList(); var inventoryItems = new List <RulesetItem>(); heroCharacter.CharacterInventory.EnumerateAllItems(inventoryItems); var attunedItems = inventoryItems.Select(i => new { Item = i, Name = i.AttunedToCharacter }).ToList(); // record item guids var heroItemGuids = heroCharacter.Items.Select(i => new { Item = i, i.Guid }).ToList(); var inventoryItemGuids = inventoryItems.Select(i => new { Item = i, i.Guid }).ToList(); try { // TODO: update to use convention = name-nnn.sav // TODO: then need UI to allow user to change name on export // For now just add EX- heroCharacter.Name = "EX-" + name; heroCharacter.BuiltIn = false; // remove active conditions (or filter out during serialization) heroCharacter.ConditionsByCategory.Clear(); // remove spells and effects (or filter out during serialization) heroCharacter.PowersUsedByMe.Clear(); heroCharacter.SpellsCastByMe.Clear(); // remove attunement, attuned items don't work well in the character inspection screen out of game foreach (var item in attunedItems) { item.Item.AttunedToCharacter = string.Empty; } // clear guids AccessTools.Field(heroCharacter.GetType(), "guid").SetValue(heroCharacter, 0UL); foreach (var item in heroItemGuids) { AccessTools.Field(item.Item.GetType(), "guid").SetValue(item.Item, 0UL); } foreach (var item in inventoryItemGuids) { AccessTools.Field(item.Item.GetType(), "guid").SetValue(item.Item, 0UL); } ServiceRepository .GetService <ICharacterPoolService>() .SaveCharacter(heroCharacter, true); } finally { // TODO: check these things are really restored // restore original values heroCharacter.Name = name; heroCharacter.BuiltIn = builtin; // restore conditions foreach (var kvp in conditions) { heroCharacter.ConditionsByCategory.Add(kvp.Key, kvp.Value); } // restore active spells and effects heroCharacter.PowersUsedByMe.AddRange(powers); heroCharacter.SpellsCastByMe.AddRange(spells); // restore attunement foreach (var item in attunedItems) { item.Item.AttunedToCharacter = item.Name; } // restore guids AccessTools.Field(heroCharacter.GetType(), "guid").SetValue(heroCharacter, guid); foreach (var item in heroItemGuids) { AccessTools.Field(item.Item.GetType(), "guid").SetValue(item.Item, item.Guid); } foreach (var item in inventoryItemGuids) { AccessTools.Field(item.Item.GetType(), "guid").SetValue(item.Item, item.Guid); } } } } }
public static bool Prefix(CharacterInspectionScreen __instance, InputCommands.Id command) { switch (command) { // Handle 'E' for 'Export' - probably make it ctrl-E case InputCommands.Id.RotateCCW: ExportInspectedCharacter(); break; } return(true); void ExportInspectedCharacter() { // TODO: UI to allow user to supply name on export, and receive feedback if name already used var hero = __instance.InspectedCharacter.RulesetCharacterHero; var firstName = hero.Name; var surName = hero.SurName; // get all names in use var usedNames = Directory .EnumerateFiles(TacticalAdventuresApplication.GameCharactersDirectory, $"*.chr") .Select(f => Path.GetFileNameWithoutExtension(f)) .ToHashSet(StringComparer.OrdinalIgnoreCase); // get the available predefined names by race and sex var racePresentation = hero.RaceDefinition.RacePresentation; var availablePredefinedFirstNames = Enumerable.Empty <string>(); switch (hero.Sex) { case RuleDefinitions.CreatureSex.Male: availablePredefinedFirstNames = racePresentation .MaleNameOptions .Select(mn => Localize(mn)) .ToHashSet(StringComparer.OrdinalIgnoreCase) .Except(usedNames) .ToList(); break; case RuleDefinitions.CreatureSex.Female: availablePredefinedFirstNames = racePresentation .FemaleNameOptions .Select(fn => Localize(fn)) .ToHashSet(StringComparer.OrdinalIgnoreCase) .Except(usedNames) .ToList(); break; default: break; } var rnd = new Random(Environment.TickCount); string newFirstName; if (availablePredefinedFirstNames.Any()) { newFirstName = availablePredefinedFirstNames .Skip(rnd.Next(availablePredefinedFirstNames.Count())) .First(); } else { // Otherwise add "-n" to current name. // Get the next save character index - let's not have over 1000 characters with the same name :) var regex = new Regex($@"^{firstName}.*-(?<num>\d{{1,4}}).chr$", RegexOptions.Singleline | RegexOptions.Compiled); var next = (Directory .EnumerateFiles(TacticalAdventuresApplication.GameCharactersDirectory, $"{firstName}*.chr") .Select(f => Path.GetFileName(f)) .Select(f => regex.Match(f)) .Where(m => m.Success) .Select(m => (int?)int.Parse(m.Groups["num"].Value)) .Max() ?? 0) + 1; newFirstName = $"{firstName}-{next}"; } string newSurname; if (racePresentation.HasSurName && racePresentation.SurNameOptions.Any()) { var availableSurnames = racePresentation.SurNameOptions .Concat(Enumerable.Repeat(surName, 1)) .Where(s => !string.IsNullOrWhiteSpace(s)) .Select(sn => Localize(sn)) .Distinct() .ToList(); newSurname = availableSurnames .Skip(rnd.Next(availableSurnames.Count)) .First(); } else { newSurname = surName; } var msg = racePresentation.HasSurName ? $"Export '{firstName} {surName}' as '{newFirstName} {newSurname}'." : $"Export '{firstName}' as '{newFirstName}'."; // TODO: localize ServiceRepository.GetService <IGuiService>().ShowMessage( MessageModal.Severity.Informative1, "Character export", msg, "OK", "Cancel", () => { Main.Log("Ok pressed"); DoExportInspectedCharacter(newFirstName, newSurname); }, () => { Main.Log("Cancel pressed"); }); } void DoExportInspectedCharacter(string newFirstName, string newSurname) { var heroCharacter = __instance.InspectedCharacter.RulesetCharacterHero; // record current name, etc.. var firstName = heroCharacter.Name; var surName = heroCharacter.SurName; var builtin = heroCharacter.BuiltIn; var guid = heroCharacter.Guid; // record current conditions, powers, spells and attunements var conditions = heroCharacter.ConditionsByCategory.ToList(); var powers = heroCharacter.PowersUsedByMe.ToList(); var spells = heroCharacter.SpellsCastByMe.ToList(); var inventoryItems = new List <RulesetItem>(); heroCharacter.CharacterInventory.EnumerateAllItems(inventoryItems); var attunedItems = inventoryItems.Select(i => new { Item = i, Name = i.AttunedToCharacter }).ToList(); // record item guids var heroItemGuids = heroCharacter.Items.Select(i => new { Item = i, i.Guid }).ToList(); var inventoryItemGuids = inventoryItems.Select(i => new { Item = i, i.Guid }).ToList(); try { heroCharacter.Name = newFirstName; heroCharacter.SurName = newSurname; heroCharacter.BuiltIn = false; // remove active conditions (or filter out during serialization) heroCharacter.ConditionsByCategory.Clear(); // remove spells and effects (or filter out during serialization) heroCharacter.PowersUsedByMe.Clear(); heroCharacter.SpellsCastByMe.Clear(); // TODO: -- need help // TODO: remove weapon modifiers and effects // TODO: fully rest and restore hit points // remove attunement, attuned items don't work well in the character inspection screen out of game foreach (var item in attunedItems) { item.Item.AttunedToCharacter = string.Empty; } // clear guids heroCharacter.SetGuid(0); foreach (var item in heroItemGuids) { item.Item.SetGuid(0); } foreach (var item in inventoryItemGuids) { item.Item.SetGuid(0); } // finally, save the character ServiceRepository .GetService <ICharacterPoolService>() .SaveCharacter(heroCharacter, true); } finally { // and finally, finally, restore everything. // TODO: check these things are really restored // restore original values heroCharacter.Name = firstName; heroCharacter.SurName = surName; heroCharacter.BuiltIn = builtin; // restore conditions foreach (var kvp in conditions) { heroCharacter.ConditionsByCategory.Add(kvp.Key, kvp.Value); } // restore active spells and effects heroCharacter.PowersUsedByMe.AddRange(powers); heroCharacter.SpellsCastByMe.AddRange(spells); // restore attunements foreach (var item in attunedItems) { item.Item.AttunedToCharacter = item.Name; } // restore guids heroCharacter.SetGuid(guid); foreach (var item in heroItemGuids) { item.Item.SetGuid(item.Guid); } foreach (var item in inventoryItemGuids) { item.Item.SetGuid(item.Guid); } } } }