Skip to content

Commit f058d95

Browse files
committed
Implemented CharacterSet FileManager
1 parent 19a129a commit f058d95

7 files changed

Lines changed: 1371 additions & 0 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
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 &lt;filename&gt; &lt;clan&gt; [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+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using Bannerlord.GameMaster.Console.Common;
2+
using Bannerlord.GameMaster.Console.Common.EntityFinding;
3+
using Bannerlord.GameMaster.Console.Common.Execution;
4+
using Bannerlord.GameMaster.Console.Common.Formatting;
5+
using Bannerlord.GameMaster.Console.Common.Parsing;
6+
using Bannerlord.GameMaster.Console.Common.Validation;
7+
using Bannerlord.GameMaster.Heroes;
8+
using System.Collections.Generic;
9+
using TaleWorlds.CampaignSystem;
10+
using TaleWorlds.Library;
11+
12+
namespace Bannerlord.GameMaster.Console.HeroCommands.HeroManagementCommands
13+
{
14+
/// <summary>
15+
/// Console command to export a hero's full character data to a single JSON file.
16+
/// Saves appearance, development, traits, battle equipment, and civilian equipment.
17+
/// </summary>
18+
public static class ExportCharacterCommand
19+
{
20+
/// <summary>
21+
/// Export a hero's full character data to a character set file.
22+
/// Usage: gm.hero.export_character &lt;hero&gt; &lt;filename&gt;
23+
/// </summary>
24+
[CommandLineFunctionality.CommandLineArgumentFunction("export_character", "gm.hero")]
25+
public static string ExportCharacter(List<string> args)
26+
{
27+
return Cmd.Run(args, () =>
28+
{
29+
// MARK: Validation
30+
if (!CommandValidator.ValidateCampaignState(out string error))
31+
return CommandResult.Error(error);
32+
33+
string usageMessage = CommandValidator.CreateUsageMessage(
34+
"gm.hero.export_character", "<hero> <filename>",
35+
"Exports a hero's full character data (appearance, development, traits, equipment) to a single JSON file.\n" +
36+
"- hero: required, hero name, stringId, or 'player' for the player hero\n" +
37+
"- filename: required, output filename without extension\n" +
38+
"Supports named arguments: hero:derthert filename:my_king\n",
39+
"gm.hero.export_character derthert my_king\n" +
40+
"gm.hero.export_character 'Ira of the Aserai' ira_backup\n" +
41+
"gm.hero.export_character hero:derthert filename:king_export");
42+
43+
ParsedArguments parsed = ArgumentParser.ParseArguments(args);
44+
45+
parsed.SetValidArguments(
46+
new ArgumentDefinition("hero", true),
47+
new ArgumentDefinition("filename", true)
48+
);
49+
50+
string validationError = parsed.GetValidationError();
51+
if (validationError != null)
52+
return CommandResult.Error(MessageFormatter.FormatErrorMessage(validationError));
53+
54+
if (parsed.TotalCount < 2)
55+
return CommandResult.Success(usageMessage);
56+
57+
// MARK: Parse Arguments
58+
string heroArg = parsed.GetArgument("hero", 0);
59+
if (string.IsNullOrWhiteSpace(heroArg))
60+
return CommandResult.Error(MessageFormatter.FormatErrorMessage("Hero argument cannot be empty."));
61+
62+
string filenameArg = parsed.GetArgument("filename", 1);
63+
if (string.IsNullOrWhiteSpace(filenameArg))
64+
return CommandResult.Error(MessageFormatter.FormatErrorMessage("Filename argument cannot be empty."));
65+
66+
// Resolve hero
67+
EntityFinderResult<Hero> heroResult = HeroFinder.FindSingleHero(heroArg);
68+
if (!heroResult.IsSuccess)
69+
return CommandResult.Error(heroResult.Message);
70+
71+
Hero hero = heroResult.Entity;
72+
73+
// MARK: Execute Logic
74+
Dictionary<string, string> resolvedValues = new()
75+
{
76+
{ "hero", hero.Name.ToString() },
77+
{ "filename", filenameArg }
78+
};
79+
80+
string argumentDisplay = parsed.FormatArgumentDisplay("gm.hero.export_character", resolvedValues);
81+
82+
CharacterSetFileManager fileManager = CharacterSetFileManager.Default;
83+
string filepath = fileManager.GetCharacterSetFilePath(filenameArg);
84+
fileManager.SaveCharacterSetToFile(hero, filepath);
85+
86+
return CommandResult.Success(argumentDisplay + MessageFormatter.FormatSuccessMessage(
87+
$"Exported character '{hero.Name}' to '{filenameArg}'.\n" +
88+
$"Hero: {hero.Name} (ID: {hero.StringId}), Level: {hero.Level}, Age: {(int)hero.Age}\n" +
89+
$"File: {filepath}"));
90+
}).Message;
91+
}
92+
}
93+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Bannerlord.GameMaster.Console.Common;
2+
using Bannerlord.GameMaster.Console.Common.Execution;
3+
using Bannerlord.GameMaster.Console.Common.Parsing;
4+
using Bannerlord.GameMaster.Console.Common.Validation;
5+
using Bannerlord.GameMaster.Heroes;
6+
using System.Collections.Generic;
7+
using System.Text;
8+
using TaleWorlds.Library;
9+
10+
namespace Bannerlord.GameMaster.Console.HeroCommands.HeroManagementCommands
11+
{
12+
/// <summary>
13+
/// Console command to list all saved character set files.
14+
/// </summary>
15+
public static class ListCharactersCommand
16+
{
17+
/// <summary>
18+
/// List all saved character set files.
19+
/// Usage: gm.hero.list_characters
20+
/// </summary>
21+
[CommandLineFunctionality.CommandLineArgumentFunction("list_characters", "gm.hero")]
22+
public static string ListCharacters(List<string> args)
23+
{
24+
return Cmd.Run(args, () =>
25+
{
26+
// MARK: Validation
27+
if (!CommandValidator.ValidateCampaignState(out string error))
28+
return CommandResult.Error(error);
29+
30+
// MARK: Execute Logic
31+
string[] files = CharacterSetFileManager.Default.ListCharacterSetFiles();
32+
33+
StringBuilder result = new();
34+
result.AppendLine("Saved Character Set Files:");
35+
36+
if (files.Length == 0)
37+
{
38+
result.AppendLine(" (No saved character set files found)");
39+
result.AppendLine($" Directory: {CharacterSetFileManager.Default.GetCharacterSetDirectory()}");
40+
}
41+
42+
else
43+
{
44+
result.AppendLine($" Found {files.Length} file(s):\n");
45+
for (int i = 0; i < files.Length; i++)
46+
{
47+
result.AppendLine($" {i + 1}. {files[i]}");
48+
}
49+
}
50+
51+
return CommandResult.Success(result.ToString());
52+
}).Message;
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)