176 lines
6.7 KiB
C#
176 lines
6.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace SessionZero.Data;
|
|
|
|
[SzfObject(SzfObjectType.CharacterTemplate, "character_template")]
|
|
public class CharacterTemplate : SzfObject
|
|
{
|
|
public string GameSystem { get; set; } = string.Empty;
|
|
public List<RequiredDatasetReference> RequiredDatasets { get; set; } = new();
|
|
public List<TemplateSection> Sections { get; set; } = new(); // Top-level sections
|
|
|
|
public override SzfObjectType ObjectType => SzfObjectType.CharacterTemplate;
|
|
|
|
public override void PopulateFromMetadata(Dictionary<string, SzfField> metadata)
|
|
{
|
|
base.PopulateFromMetadata(metadata);
|
|
|
|
if (metadata.TryGetValue("GameSystem", out var gameSystemField))
|
|
GameSystem = gameSystemField.Value?.ToString() ?? string.Empty;
|
|
}
|
|
|
|
public override void ParseSections(List<SzfSection> sections)
|
|
{
|
|
// Parse "Required Datasets" section
|
|
var requiredDatasetsSection =
|
|
sections.FirstOrDefault(s => s.Name.Equals("Required Datasets", StringComparison.OrdinalIgnoreCase));
|
|
if (requiredDatasetsSection != null)
|
|
{
|
|
foreach (var field in requiredDatasetsSection.Fields)
|
|
{
|
|
var datasetReference = DatasetReference.Parse(field.Value?.ToString() ?? string.Empty);
|
|
if (datasetReference != null)
|
|
{
|
|
RequiredDatasets.Add(new RequiredDatasetReference
|
|
{
|
|
Alias = field.Name,
|
|
Reference = datasetReference
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse and build the hierarchical template sections
|
|
// Use a map to quickly find existing sections by their full path for nesting
|
|
var sectionPathMap = new Dictionary<string, TemplateSection>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (var szfSection in sections)
|
|
{
|
|
// Only process sections that start with "Section:"
|
|
if (!szfSection.Name.StartsWith("Section:", StringComparison.OrdinalIgnoreCase)) continue;
|
|
|
|
// Normalize the section name (e.g., "Section:Ability Scores.Modifiers" to "Ability Scores.Modifiers")
|
|
var rawSectionPath = szfSection.Name.Substring("Section:".Length).Trim();
|
|
var nameParts = rawSectionPath.Split('.');
|
|
|
|
TemplateSection? currentParentSection = null;
|
|
List<TemplateSection> currentSectionList = Sections; // Start from the top-level Sections list
|
|
|
|
string accumulatedPath = "Section"; // Used for dictionary key to ensure uniqueness and hierarchy
|
|
|
|
for (int i = 0; i < nameParts.Length; i++)
|
|
{
|
|
var sectionNamePart = nameParts[i];
|
|
accumulatedPath += "." + sectionNamePart; // Build the full path for the map key
|
|
|
|
TemplateSection? targetSection = null;
|
|
|
|
// Check if this specific section (at this level of hierarchy) already exists
|
|
if (sectionPathMap.TryGetValue(accumulatedPath, out var existingSection))
|
|
{
|
|
targetSection = existingSection;
|
|
}
|
|
else
|
|
{
|
|
// Create a new section if it doesn't exist
|
|
var newSection = new TemplateSection { Name = sectionNamePart };
|
|
sectionPathMap[accumulatedPath] = newSection;
|
|
|
|
if (currentParentSection == null)
|
|
{
|
|
// This is a top-level section
|
|
currentSectionList.Add(newSection);
|
|
}
|
|
else
|
|
{
|
|
// This is a subsection of the current parent
|
|
currentParentSection.Subsections.Add(newSection);
|
|
}
|
|
|
|
targetSection = newSection;
|
|
}
|
|
|
|
currentParentSection = targetSection; // Move down the hierarchy
|
|
currentSectionList =
|
|
targetSection.Subsections; // Next iteration will look in subsections of this section
|
|
}
|
|
|
|
// Once we've traversed/created the full path, currentParentSection points to the innermost section.
|
|
// Now populate its fields from the SzfSection.
|
|
if (currentParentSection != null)
|
|
{
|
|
foreach (var field in szfSection.Fields)
|
|
{
|
|
currentParentSection.Fields.Add(ConvertToTemplateField(field));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private TemplateField ConvertToTemplateField(SzfField szfField)
|
|
{
|
|
return new TemplateField
|
|
{
|
|
Name = szfField.Name,
|
|
Type = ParseDatasetFieldType(szfField.Type), // Reusing DatasetFieldType for consistency
|
|
Value = szfField.Value,
|
|
};
|
|
}
|
|
|
|
// Helper method to parse DatasetFieldType, can be shared or defined here.
|
|
// Copying from Dataset.cs for now, consider a common utility.
|
|
private DatasetFieldType ParseDatasetFieldType(string typeString)
|
|
{
|
|
return typeString.ToLower() switch
|
|
{
|
|
"text" => DatasetFieldType.Text,
|
|
"text-field" => DatasetFieldType.TextField,
|
|
"number" => DatasetFieldType.Number,
|
|
"bool" => DatasetFieldType.Boolean,
|
|
"group" => DatasetFieldType.Group,
|
|
"calculated" => DatasetFieldType.Calculated,
|
|
"system" => DatasetFieldType.System,
|
|
"dataset-reference" => DatasetFieldType.DatasetReference,
|
|
"dataset-type" => DatasetFieldType.DatasetType,
|
|
"dataset-reference-multiple" => DatasetFieldType.DatasetReferenceMultiple,
|
|
"dataset-type-multiple" => DatasetFieldType.DatasetTypeMultiple,
|
|
_ => DatasetFieldType.Text
|
|
};
|
|
}
|
|
|
|
public override SzfValidationResult Validate()
|
|
{
|
|
var result = base.Validate();
|
|
|
|
// if (string.IsNullOrWhiteSpace(GameSystem))
|
|
// result.AddError("GameSystem is required for CharacterTemplate");
|
|
|
|
// Further validation for required datasets and template sections can be added here
|
|
// e.g., checking for duplicate section names or missing required fields.
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public class RequiredDatasetReference
|
|
{
|
|
public string Alias { get; set; } = string.Empty;
|
|
public DatasetReference? Reference { get; set; }
|
|
}
|
|
|
|
public class TemplateSection
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public List<TemplateField> Fields { get; set; } = new();
|
|
public List<TemplateSection> Subsections { get; set; } = new();
|
|
}
|
|
|
|
public class TemplateField
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public DatasetFieldType Type { get; set; }
|
|
public object? Value { get; set; }
|
|
} |