Merge branch 'phase/47-romance-pack'
ci / server (push) Failing after 3m39s
ci / client (push) Successful in 19s

# Conflicts:
#	docs/phases/README.md
#	src/HSchool.Content/PeopleDefs.cs
#	src/HSchool.People/Seed.cs
This commit is contained in:
Leonid Pershin
2026-08-20 11:00:12 +03:00
44 changed files with 1841 additions and 30 deletions
+69
View File
@@ -70,6 +70,16 @@ internal static class PeopleDefValidator
ValidateTopic(topic, catalog);
}
foreach (var orientation in catalog.Orientations.Values)
{
ValidateOrientation(orientation);
}
foreach (var affinity in catalog.Affinity.Values)
{
ValidateAffinityRules(affinity);
}
RequireTalkTopics(catalog);
if (catalog.Staffing.Values.Count(def => !def.Abstract) > 1)
@@ -87,6 +97,11 @@ internal static class PeopleDefValidator
throw new ContentLoadException("A catalog may only have one concrete BehaviorDef.");
}
if (catalog.Affinity.Values.Count(def => !def.Abstract) > 1)
{
throw new ContentLoadException("A catalog may only have one concrete AffinityRulesDef.");
}
RequireBuildInputs(catalog);
}
@@ -645,6 +660,60 @@ internal static class PeopleDefValidator
}
}
private static void ValidateOrientation(OrientationDef orientation)
{
if (orientation.Abstract)
{
return;
}
if (orientation.Weight < 1)
{
throw new ContentLoadException($"OrientationDef '{orientation.DefName}' weight must be at least 1.");
}
if (!orientation.SameGender && !orientation.OppositeGender)
{
throw new ContentLoadException(
$"OrientationDef '{orientation.DefName}' must allow same gender, opposite gender, or both.");
}
}
private static void ValidateAffinityRules(AffinityRulesDef rules)
{
if (rules.Abstract)
{
return;
}
if (rules.SympathyThreshold < OpinionLabelsMin || rules.PairThreshold < OpinionLabelsMin
|| rules.PairBreakThreshold < OpinionLabelsMin)
{
throw new ContentLoadException($"AffinityRulesDef '{rules.DefName}' opinion thresholds are out of range.");
}
if (rules.PairMinAge < 0)
{
throw new ContentLoadException($"AffinityRulesDef '{rules.DefName}' pairMinAge cannot be negative.");
}
if (rules.StudentAgeDeltaYears < 0)
{
throw new ContentLoadException($"AffinityRulesDef '{rules.DefName}' studentAgeDeltaYears cannot be negative.");
}
foreach (var pair in rules.RolePairs)
{
if (!PersonRoles.IsKnown(pair.From) || !PersonRoles.IsKnown(pair.To))
{
throw new ContentLoadException(
$"AffinityRulesDef '{rules.DefName}' role pair '{pair.From}'→'{pair.To}' uses an unknown role.");
}
}
}
private const int OpinionLabelsMin = -100;
/// <summary>A catalog with circle actions but no topics would silently solo-chat — refuse load.</summary>
private static void RequireTalkTopics(DefCatalog catalog)
{