|
| 1 | +using Bannerlord.GameMaster.Common; |
| 2 | +using Bannerlord.GameMaster.Console.Common; |
| 3 | +using Bannerlord.GameMaster.Console.Common.EntityFinding; |
| 4 | +using Bannerlord.GameMaster.Console.Common.Execution; |
| 5 | +using Bannerlord.GameMaster.Console.Common.Formatting; |
| 6 | +using Bannerlord.GameMaster.Console.Common.Parsing; |
| 7 | +using Bannerlord.GameMaster.Console.Common.Validation; |
| 8 | +using Bannerlord.GameMaster.Heroes; |
| 9 | +using System.Collections.Generic; |
| 10 | +using TaleWorlds.CampaignSystem; |
| 11 | +using TaleWorlds.CampaignSystem.Settlements; |
| 12 | +using TaleWorlds.Library; |
| 13 | + |
| 14 | +namespace Bannerlord.GameMaster.Console.HeroCommands.HeroGenerationCommands |
| 15 | +{ |
| 16 | + /// <summary> |
| 17 | + /// Console command to import a character set file and create a new hero from it. |
| 18 | + /// </summary> |
| 19 | + public static class ImportCharacterCommand |
| 20 | + { |
| 21 | + /// <summary> |
| 22 | + /// Import a character set file to create a new hero. |
| 23 | + /// Usage: gm.hero.import_character <filename> <clan> [type] [settlement] [withParty] |
| 24 | + /// </summary> |
| 25 | + [CommandLineFunctionality.CommandLineArgumentFunction("import_character", "gm.hero")] |
| 26 | + public static string ImportCharacter(List<string> args) |
| 27 | + { |
| 28 | + return Cmd.Run(args, () => |
| 29 | + { |
| 30 | + // MARK: Validation |
| 31 | + if (!CommandValidator.ValidateCampaignState(out string error)) |
| 32 | + return CommandResult.Error(error); |
| 33 | + |
| 34 | + string usageMessage = CommandValidator.CreateUsageMessage( |
| 35 | + "gm.hero.import_character", "<filename> <clan> [type] [settlement] [withParty]", |
| 36 | + "Creates a new hero from a previously exported character set file.\n" + |
| 37 | + "The saved appearance, development, traits, and equipment are applied to the new hero.\n" + |
| 38 | + "- filename: required, character set filename (without extension)\n" + |
| 39 | + "- clan: required, target clan name, stringId, or partial match\n" + |
| 40 | + "- type: optional, override hero type: lord, wanderer, companion. Defaults to saved type\n" + |
| 41 | + "- settlement: optional, placement settlement name or ID. Defaults to auto-resolved from clan\n" + |
| 42 | + "- withParty/party: optional, for lords: create party (true/false). Defaults to true\n" + |
| 43 | + "Supports named arguments: filename:my_king clan:meroc type:lord settlement:Poros withParty:true\n", |
| 44 | + "gm.hero.import_character my_king 'dey meroc'\n" + |
| 45 | + "gm.hero.import_character king_export meroc lord\n" + |
| 46 | + "gm.hero.import_character filename:ira_backup clan:meroc type:companion\n" + |
| 47 | + "gm.hero.import_character ira_backup 'dey meroc' wanderer Poros\n" + |
| 48 | + "gm.hero.import_character ira_backup meroc lord 'Vladiv Castle' false"); |
| 49 | + |
| 50 | + ParsedArguments parsed = ArgumentParser.ParseArguments(args); |
| 51 | + |
| 52 | + parsed.SetValidArguments( |
| 53 | + new ArgumentDefinition("filename", true), |
| 54 | + new ArgumentDefinition("clan", true), |
| 55 | + new ArgumentDefinition("type", false), |
| 56 | + new ArgumentDefinition("settlement", false), |
| 57 | + new ArgumentDefinition("withParty", false, null, "party") |
| 58 | + ); |
| 59 | + |
| 60 | + string validationError = parsed.GetValidationError(); |
| 61 | + if (validationError != null) |
| 62 | + return CommandResult.Error(MessageFormatter.FormatErrorMessage(validationError)); |
| 63 | + |
| 64 | + if (parsed.TotalCount < 2) |
| 65 | + return CommandResult.Success(usageMessage); |
| 66 | + |
| 67 | + // MARK: Parse Arguments |
| 68 | + string filenameArg = parsed.GetArgument("filename", 0); |
| 69 | + if (string.IsNullOrWhiteSpace(filenameArg)) |
| 70 | + return CommandResult.Error(MessageFormatter.FormatErrorMessage("Filename argument cannot be empty.")); |
| 71 | + |
| 72 | + // Verify file exists |
| 73 | + CharacterSetFileManager fileManager = CharacterSetFileManager.Default; |
| 74 | + if (!fileManager.CharacterSetFileExists(filenameArg)) |
| 75 | + { |
| 76 | + return CommandResult.Error(MessageFormatter.FormatErrorMessage( |
| 77 | + $"Character set file '{filenameArg}' not found.\n" + |
| 78 | + $"Directory: {fileManager.GetCharacterSetDirectory()}")); |
| 79 | + } |
| 80 | + |
| 81 | + // Resolve clan |
| 82 | + string clanArg = parsed.GetArgument("clan", 1); |
| 83 | + if (string.IsNullOrWhiteSpace(clanArg)) |
| 84 | + return CommandResult.Error(MessageFormatter.FormatErrorMessage("Clan argument cannot be empty.")); |
| 85 | + |
| 86 | + EntityFinderResult<Clan> clanResult = ClanFinder.FindSingleClan(clanArg); |
| 87 | + if (!clanResult.IsSuccess) |
| 88 | + return CommandResult.Error(clanResult.Message); |
| 89 | + |
| 90 | + Clan targetClan = clanResult.Entity; |
| 91 | + |
| 92 | + // Parse type override |
| 93 | + string typeArg = parsed.GetArgument("type", 2); |
| 94 | + if (typeArg != null) |
| 95 | + { |
| 96 | + string lowerType = typeArg.ToLower(); |
| 97 | + if (lowerType != "lord" && lowerType != "wanderer" && lowerType != "companion") |
| 98 | + { |
| 99 | + return CommandResult.Error(MessageFormatter.FormatErrorMessage( |
| 100 | + $"Invalid hero type '{typeArg}'. Valid types: lord, wanderer, companion")); |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + // Parse settlement |
| 105 | + Settlement settlement = null; |
| 106 | + string settlementArg = parsed.GetArgument("settlement", 3); |
| 107 | + if (settlementArg != null && settlementArg.ToLower() != "null") |
| 108 | + { |
| 109 | + EntityFinderResult<Settlement> settlementResult = SettlementFinder.FindSingleSettlement(settlementArg); |
| 110 | + if (!settlementResult.IsSuccess) |
| 111 | + return CommandResult.Error(settlementResult.Message); |
| 112 | + |
| 113 | + settlement = settlementResult.Entity; |
| 114 | + } |
| 115 | + |
| 116 | + // Parse withParty |
| 117 | + bool withParty = true; |
| 118 | + string withPartyArg = parsed.GetArgument("withParty", 4) ?? parsed.GetNamed("party"); |
| 119 | + if (withPartyArg != null) |
| 120 | + { |
| 121 | + if (!CommandValidator.ValidateBoolean(withPartyArg, out withParty, out string boolError)) |
| 122 | + return CommandResult.Error(MessageFormatter.FormatErrorMessage(boolError)); |
| 123 | + } |
| 124 | + |
| 125 | + if (!CommandValidator.ValidateHeroCreationLimit(1, out string limitError)) |
| 126 | + return CommandResult.Error(MessageFormatter.FormatErrorMessage(limitError)); |
| 127 | + |
| 128 | + // MARK: Execute Logic |
| 129 | + Dictionary<string, string> resolvedValues = new() |
| 130 | + { |
| 131 | + { "filename", filenameArg }, |
| 132 | + { "clan", targetClan.Name.ToString() }, |
| 133 | + { "type", typeArg ?? "From file" }, |
| 134 | + { "settlement", settlement != null ? settlement.Name.ToString() : "Auto-resolved" }, |
| 135 | + { "withParty", withParty.ToString() } |
| 136 | + }; |
| 137 | + |
| 138 | + string argumentDisplay = parsed.FormatArgumentDisplay("gm.hero.import_character", resolvedValues); |
| 139 | + |
| 140 | + string filepath = fileManager.GetCharacterSetFilePath(filenameArg); |
| 141 | + BLGMResult importResult = fileManager.ImportCharacterSet(filepath, targetClan, typeArg, settlement, withParty); |
| 142 | + |
| 143 | + if (!importResult.IsSuccess) |
| 144 | + return CommandResult.Error(argumentDisplay + MessageFormatter.FormatErrorMessage(importResult.Message)); |
| 145 | + |
| 146 | + return CommandResult.Success(argumentDisplay + MessageFormatter.FormatSuccessMessage(importResult.Message)); |
| 147 | + }).Message; |
| 148 | + } |
| 149 | + } |
| 150 | +} |
0 commit comments