Merge phase 29 country.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -307,7 +307,8 @@ public sealed class CatalogLoader
|
||||
var traits = new Dictionary<string, TraitDef>(StringComparer.Ordinal);
|
||||
var bodyAttributes = new Dictionary<string, BodyAttributeDef>(StringComparer.Ordinal);
|
||||
var needs = new Dictionary<string, NeedDef>(StringComparer.Ordinal);
|
||||
var nameSets = new Dictionary<string, NameSetDef>(StringComparer.Ordinal);
|
||||
var countries = new Dictionary<string, CountryDef>(StringComparer.Ordinal);
|
||||
var climatePresets = new Dictionary<string, ClimatePresetDef>(StringComparer.Ordinal);
|
||||
var subjects = new Dictionary<string, SubjectDef>(StringComparer.Ordinal);
|
||||
var staffing = new Dictionary<string, StaffingDef>(StringComparer.Ordinal);
|
||||
var dayFrames = new Dictionary<string, DayFrameDef>(StringComparer.Ordinal);
|
||||
@@ -355,8 +356,11 @@ public sealed class CatalogLoader
|
||||
case DefKind.Need:
|
||||
needs[key.Name] = Jsonc.Deserialize<NeedDef>(json);
|
||||
break;
|
||||
case DefKind.NameSet:
|
||||
nameSets[key.Name] = Jsonc.Deserialize<NameSetDef>(json);
|
||||
case DefKind.Country:
|
||||
countries[key.Name] = Jsonc.Deserialize<CountryDef>(json);
|
||||
break;
|
||||
case DefKind.ClimatePreset:
|
||||
climatePresets[key.Name] = Jsonc.Deserialize<ClimatePresetDef>(json);
|
||||
break;
|
||||
case DefKind.Subject:
|
||||
subjects[key.Name] = Jsonc.Deserialize<SubjectDef>(json);
|
||||
@@ -393,7 +397,8 @@ public sealed class CatalogLoader
|
||||
traits,
|
||||
bodyAttributes,
|
||||
needs,
|
||||
nameSets,
|
||||
countries,
|
||||
climatePresets,
|
||||
subjects,
|
||||
staffing,
|
||||
dayFrames,
|
||||
@@ -541,7 +546,8 @@ public sealed class CatalogLoader
|
||||
.Concat(Enumerate(catalog.Traits.Values))
|
||||
.Concat(Enumerate(catalog.BodyAttributes.Values))
|
||||
.Concat(Enumerate(catalog.Needs.Values))
|
||||
.Concat(Enumerate(catalog.NameSets.Values))
|
||||
.Concat(Enumerate(catalog.Countries.Values))
|
||||
.Concat(Enumerate(catalog.ClimatePresets.Values))
|
||||
.Concat(Enumerate(catalog.Subjects.Values))
|
||||
.Concat(Enumerate(catalog.Staffing.Values))
|
||||
.Concat(Enumerate(catalog.DayFrames.Values))
|
||||
|
||||
@@ -20,7 +20,8 @@ public sealed class DefCatalog
|
||||
IReadOnlyDictionary<string, TraitDef> traits,
|
||||
IReadOnlyDictionary<string, BodyAttributeDef> bodyAttributes,
|
||||
IReadOnlyDictionary<string, NeedDef> needs,
|
||||
IReadOnlyDictionary<string, NameSetDef> nameSets,
|
||||
IReadOnlyDictionary<string, CountryDef> countries,
|
||||
IReadOnlyDictionary<string, ClimatePresetDef> climatePresets,
|
||||
IReadOnlyDictionary<string, SubjectDef> subjects,
|
||||
IReadOnlyDictionary<string, StaffingDef> staffing,
|
||||
IReadOnlyDictionary<string, DayFrameDef> dayFrames,
|
||||
@@ -43,7 +44,8 @@ public sealed class DefCatalog
|
||||
Traits = traits;
|
||||
BodyAttributes = bodyAttributes;
|
||||
Needs = needs;
|
||||
NameSets = nameSets;
|
||||
Countries = countries;
|
||||
ClimatePresets = climatePresets;
|
||||
Subjects = subjects;
|
||||
Staffing = staffing;
|
||||
DayFrames = dayFrames;
|
||||
@@ -91,7 +93,9 @@ public sealed class DefCatalog
|
||||
|
||||
public IReadOnlyDictionary<string, NeedDef> Needs { get; }
|
||||
|
||||
public IReadOnlyDictionary<string, NameSetDef> NameSets { get; }
|
||||
public IReadOnlyDictionary<string, CountryDef> Countries { get; }
|
||||
|
||||
public IReadOnlyDictionary<string, ClimatePresetDef> ClimatePresets { get; }
|
||||
|
||||
public IReadOnlyDictionary<string, SubjectDef> Subjects { get; }
|
||||
|
||||
@@ -133,7 +137,8 @@ public sealed class DefCatalog
|
||||
DefKind.Trait => Traits.GetValueOrDefault(defName),
|
||||
DefKind.BodyAttribute => BodyAttributes.GetValueOrDefault(defName),
|
||||
DefKind.Need => Needs.GetValueOrDefault(defName),
|
||||
DefKind.NameSet => NameSets.GetValueOrDefault(defName),
|
||||
DefKind.Country => Countries.GetValueOrDefault(defName),
|
||||
DefKind.ClimatePreset => ClimatePresets.GetValueOrDefault(defName),
|
||||
DefKind.Subject => Subjects.GetValueOrDefault(defName),
|
||||
DefKind.Staffing => Staffing.GetValueOrDefault(defName),
|
||||
DefKind.DayFrame => DayFrames.GetValueOrDefault(defName),
|
||||
@@ -212,7 +217,8 @@ public sealed class DefCatalog
|
||||
TraitDef => DefKind.Trait,
|
||||
BodyAttributeDef => DefKind.BodyAttribute,
|
||||
NeedDef => DefKind.Need,
|
||||
NameSetDef => DefKind.NameSet,
|
||||
CountryDef => DefKind.Country,
|
||||
ClimatePresetDef => DefKind.ClimatePreset,
|
||||
SubjectDef => DefKind.Subject,
|
||||
StaffingDef => DefKind.Staffing,
|
||||
DayFrameDef => DefKind.DayFrame,
|
||||
@@ -245,4 +251,17 @@ public sealed class DefCatalog
|
||||
set.Remove(defName);
|
||||
return set;
|
||||
}
|
||||
|
||||
/// <summary>Nested names of a concrete country, or false when the id is missing or abstract.</summary>
|
||||
public bool TryGetCountryNames(string countryId, out NameSetDef names)
|
||||
{
|
||||
if (Countries.TryGetValue(countryId, out var country) && !country.Abstract)
|
||||
{
|
||||
names = country.Names;
|
||||
return true;
|
||||
}
|
||||
|
||||
names = null!;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ public enum DefKind
|
||||
Trait,
|
||||
BodyAttribute,
|
||||
Need,
|
||||
NameSet,
|
||||
Country,
|
||||
ClimatePreset,
|
||||
Subject,
|
||||
Staffing,
|
||||
DayFrame,
|
||||
|
||||
@@ -105,8 +105,11 @@ internal static class PackPaths
|
||||
case "needs":
|
||||
kind = DefKind.Need;
|
||||
return true;
|
||||
case "namesets":
|
||||
kind = DefKind.NameSet;
|
||||
case "countries":
|
||||
kind = DefKind.Country;
|
||||
return true;
|
||||
case "climates":
|
||||
kind = DefKind.ClimatePreset;
|
||||
return true;
|
||||
case "subjects":
|
||||
kind = DefKind.Subject;
|
||||
|
||||
@@ -25,9 +25,9 @@ internal static class PeopleDefValidator
|
||||
ValidateNeed(need);
|
||||
}
|
||||
|
||||
foreach (var names in catalog.NameSets.Values)
|
||||
foreach (var country in catalog.Countries.Values)
|
||||
{
|
||||
ValidateNameSet(names, catalog);
|
||||
ValidateCountry(country, catalog);
|
||||
}
|
||||
|
||||
foreach (var subject in catalog.Subjects.Values)
|
||||
@@ -547,61 +547,90 @@ internal static class PeopleDefValidator
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateNameSet(NameSetDef names, DefCatalog catalog)
|
||||
private static void ValidateCountry(CountryDef country, DefCatalog catalog)
|
||||
{
|
||||
if (country.Abstract)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (country.ClimatePresets.Count == 0)
|
||||
{
|
||||
throw new ContentLoadException($"CountryDef '{country.DefName}' needs at least one climate preset.");
|
||||
}
|
||||
|
||||
var seen = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach (var presetId in country.ClimatePresets)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(presetId) || !seen.Add(presetId))
|
||||
{
|
||||
throw new ContentLoadException($"CountryDef '{country.DefName}' has a missing or duplicate climate preset.");
|
||||
}
|
||||
|
||||
if (!catalog.ClimatePresets.TryGetValue(presetId, out var preset) || preset.Abstract)
|
||||
{
|
||||
throw new ContentLoadException($"CountryDef '{country.DefName}' references unknown ClimatePresetDef '{presetId}'.");
|
||||
}
|
||||
}
|
||||
|
||||
ValidateNameSet(country.Names ?? new NameSetDef(), catalog, country.DefName);
|
||||
}
|
||||
|
||||
private static void ValidateNameSet(NameSetDef names, DefCatalog catalog, string countryDefName)
|
||||
{
|
||||
if (!NameGrammar.IsKnownPatronymic(names.PatronymicRule))
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' has unknown patronymicRule '{names.PatronymicRule}'.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' has unknown patronymicRule '{names.PatronymicRule}'.");
|
||||
}
|
||||
|
||||
foreach (var native in names.Spoken)
|
||||
{
|
||||
if (!catalog.Skills.TryGetValue(native, out var language) || language.Abstract)
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' native language '{native}' is not a SkillDef.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' native language '{native}' is not a SkillDef.");
|
||||
}
|
||||
}
|
||||
|
||||
if (names.RelatedLanguageChance is < 0 or > 1)
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' relatedLanguageChance must be 0–1.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' relatedLanguageChance must be 0–1.");
|
||||
}
|
||||
|
||||
if (names.RelatedLanguageStdDev < 0)
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' relatedLanguageStdDev must not be negative.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' relatedLanguageStdDev must not be negative.");
|
||||
}
|
||||
|
||||
if (names.RelatedLanguageMean < 0)
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' relatedLanguageMean must not be negative.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' relatedLanguageMean must not be negative.");
|
||||
}
|
||||
|
||||
if (names.RelatedLanguageMax < 0)
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' relatedLanguageMax must not be negative.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' relatedLanguageMax must not be negative.");
|
||||
}
|
||||
|
||||
if (!NameGrammar.IsKnownGiven(names.DefaultGivenDeclension))
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' has unknown defaultGivenDeclension.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' has unknown defaultGivenDeclension.");
|
||||
}
|
||||
|
||||
if (!NameGrammar.IsKnownSurname(names.DefaultSurnameDeclension))
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' has unknown defaultSurnameDeclension.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' has unknown defaultSurnameDeclension.");
|
||||
}
|
||||
|
||||
if (names.MaleGiven.Count == 0 || names.FemaleGiven.Count == 0 || names.Surnames.Count == 0)
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' needs male names, female names and surnames.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' needs male names, female names and surnames.");
|
||||
}
|
||||
|
||||
foreach (var given in names.MaleGiven.Concat(names.FemaleGiven))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(given.Form))
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' has an empty given name.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' has an empty given name.");
|
||||
}
|
||||
|
||||
if (given.Cases is null)
|
||||
@@ -611,7 +640,7 @@ internal static class PeopleDefValidator
|
||||
: given.Declension;
|
||||
if (!NameGrammar.IsKnownGiven(model))
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' given '{given.Form}' has unknown declension '{model}'.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' given '{given.Form}' has unknown declension '{model}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -620,7 +649,7 @@ internal static class PeopleDefValidator
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(surname.Male) || string.IsNullOrWhiteSpace(surname.Female))
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' has a surname missing a gendered form.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' has a surname missing a gendered form.");
|
||||
}
|
||||
|
||||
if (surname.MaleCases is null && surname.FemaleCases is null)
|
||||
@@ -630,7 +659,7 @@ internal static class PeopleDefValidator
|
||||
: surname.Declension;
|
||||
if (!NameGrammar.IsKnownSurname(model))
|
||||
{
|
||||
throw new ContentLoadException($"NameSetDef '{names.DefName}' surname '{surname.Male}' has unknown declension '{model}'.");
|
||||
throw new ContentLoadException($"CountryDef '{countryDefName}' surname '{surname.Male}' has unknown declension '{model}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +359,10 @@ public sealed class SurnameEntry
|
||||
public CaseTable? FemaleCases { get; init; }
|
||||
}
|
||||
|
||||
public sealed class NameSetDef : Def
|
||||
/// <summary>
|
||||
/// Nested name grammar of a <see cref="CountryDef"/>. Not a selectable catalog kind of its own.
|
||||
/// </summary>
|
||||
public sealed class NameSetDef
|
||||
{
|
||||
public string PatronymicRule { get; init; } = NameGrammar.SlavicPatronymic;
|
||||
|
||||
@@ -368,7 +371,7 @@ public sealed class NameSetDef : Def
|
||||
public string DefaultSurnameDeclension { get; init; } = NameGrammar.Ov;
|
||||
|
||||
/// <summary>
|
||||
/// Languages this set can speak natively. Slavic names cover Russian, Belarusian and
|
||||
/// Languages this country can speak natively. Slavic names cover Russian, Belarusian and
|
||||
/// Ukrainian; the school picks one at create. Singular <see cref="NativeLanguage"/> is still
|
||||
/// accepted in JSONC for a one-language pack.
|
||||
/// </summary>
|
||||
@@ -401,3 +404,20 @@ public sealed class NameSetDef : Def
|
||||
|
||||
public IReadOnlyList<SurnameEntry> Surnames { get; init; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What the player picks at create: nested names plus climate-preset ids. Weather numbers live
|
||||
/// on <see cref="ClimatePresetDef"/> and stay unused until phase 32.
|
||||
/// </summary>
|
||||
public sealed class CountryDef : Def
|
||||
{
|
||||
public IReadOnlyList<string> ClimatePresets { get; init; } = [];
|
||||
|
||||
public NameSetDef Names { get; init; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outdoor climate a country may roll. Monthly temperatures land in phase 32; the id is enough
|
||||
/// to persist which preset a school was born with.
|
||||
/// </summary>
|
||||
public sealed class ClimatePresetDef : Def;
|
||||
|
||||
Reference in New Issue
Block a user